@tinycloud/node-sdk 2.7.0-beta.3 → 2.7.0-beta.4

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/core.cjs CHANGED
@@ -30,103 +30,105 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/core.ts
31
31
  var core_exports = {};
32
32
  __export(core_exports, {
33
- ACCOUNT_REGISTRY_PATH: () => import_sdk_core11.ACCOUNT_REGISTRY_PATH,
34
- ACCOUNT_REGISTRY_SPACE: () => import_sdk_core11.ACCOUNT_REGISTRY_SPACE,
33
+ ACCOUNT_REGISTRY_PATH: () => import_sdk_core12.ACCOUNT_REGISTRY_PATH,
34
+ ACCOUNT_REGISTRY_SPACE: () => import_sdk_core12.ACCOUNT_REGISTRY_SPACE,
35
35
  AccountService: () => import_sdk_core4.AccountService,
36
- AutoApproveSpaceCreationHandler: () => import_sdk_core10.AutoApproveSpaceCreationHandler,
37
- CapabilityKeyRegistry: () => import_sdk_core17.CapabilityKeyRegistry,
38
- CapabilityKeyRegistryErrorCodes: () => import_sdk_core17.CapabilityKeyRegistryErrorCodes,
39
- DEFAULT_MANIFEST_SPACE: () => import_sdk_core11.DEFAULT_MANIFEST_SPACE,
40
- DEFAULT_MANIFEST_VERSION: () => import_sdk_core11.DEFAULT_MANIFEST_VERSION,
41
- DEFAULT_SIGNED_READ_URL_EXPIRY_MS: () => import_sdk_core12.DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
42
- DataVaultService: () => import_sdk_core15.DataVaultService,
43
- DatabaseHandle: () => import_sdk_core13.DatabaseHandle,
36
+ AutoApproveSpaceCreationHandler: () => import_sdk_core11.AutoApproveSpaceCreationHandler,
37
+ CapabilityKeyRegistry: () => import_sdk_core18.CapabilityKeyRegistry,
38
+ CapabilityKeyRegistryErrorCodes: () => import_sdk_core18.CapabilityKeyRegistryErrorCodes,
39
+ CaveatedDelegationUnsupportedError: () => import_sdk_core12.CaveatedDelegationUnsupportedError,
40
+ DEFAULT_MANIFEST_SPACE: () => import_sdk_core12.DEFAULT_MANIFEST_SPACE,
41
+ DEFAULT_MANIFEST_VERSION: () => import_sdk_core12.DEFAULT_MANIFEST_VERSION,
42
+ DEFAULT_SIGNED_READ_URL_EXPIRY_MS: () => import_sdk_core13.DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
43
+ DataVaultService: () => import_sdk_core16.DataVaultService,
44
+ DatabaseHandle: () => import_sdk_core14.DatabaseHandle,
44
45
  DelegatedAccess: () => DelegatedAccess,
45
- DelegationErrorCodes: () => import_sdk_core16.DelegationErrorCodes,
46
- DelegationManager: () => import_sdk_core16.DelegationManager,
47
- DuckDbAction: () => import_sdk_core14.DuckDbAction,
48
- DuckDbDatabaseHandle: () => import_sdk_core14.DuckDbDatabaseHandle,
49
- DuckDbService: () => import_sdk_core14.DuckDbService,
46
+ DelegationErrorCodes: () => import_sdk_core17.DelegationErrorCodes,
47
+ DelegationManager: () => import_sdk_core17.DelegationManager,
48
+ DuckDbAction: () => import_sdk_core15.DuckDbAction,
49
+ DuckDbDatabaseHandle: () => import_sdk_core15.DuckDbDatabaseHandle,
50
+ DuckDbService: () => import_sdk_core15.DuckDbService,
50
51
  FileSessionStorage: () => FileSessionStorage,
51
- IdentityParseError: () => import_sdk_core10.IdentityParseError,
52
- KVService: () => import_sdk_core12.KVService,
53
- ManifestValidationError: () => import_sdk_core11.ManifestValidationError,
52
+ IdentityParseError: () => import_sdk_core11.IdentityParseError,
53
+ KVService: () => import_sdk_core13.KVService,
54
+ ManifestValidationError: () => import_sdk_core12.ManifestValidationError,
54
55
  MemorySessionStorage: () => MemorySessionStorage,
55
56
  NodeUserAuthorization: () => NodeUserAuthorization,
56
- PermissionNotInManifestError: () => import_sdk_core11.PermissionNotInManifestError,
57
- PrefixedKVService: () => import_sdk_core12.PrefixedKVService,
58
- ProtocolMismatchError: () => import_sdk_core19.ProtocolMismatchError,
59
- SECRET_NAME_RE: () => import_sdk_core15.SECRET_NAME_RE,
60
- SQLAction: () => import_sdk_core13.SQLAction,
61
- SQLService: () => import_sdk_core13.SQLService,
62
- SecretsService: () => import_sdk_core15.SecretsService,
63
- ServiceContext: () => import_sdk_core20.ServiceContext,
64
- SessionExpiredError: () => import_sdk_core11.SessionExpiredError,
65
- SharingService: () => import_sdk_core16.SharingService,
66
- SilentNotificationHandler: () => import_sdk_core10.SilentNotificationHandler,
67
- Space: () => import_sdk_core18.Space,
68
- SpaceErrorCodes: () => import_sdk_core18.SpaceErrorCodes,
69
- SpaceService: () => import_sdk_core18.SpaceService,
70
- TinyCloud: () => import_sdk_core9.TinyCloud,
71
- TinyCloudDebugLogger: () => import_sdk_core10.TinyCloudDebugLogger,
57
+ PermissionNotInManifestError: () => import_sdk_core12.PermissionNotInManifestError,
58
+ PrefixedKVService: () => import_sdk_core13.PrefixedKVService,
59
+ ProtocolMismatchError: () => import_sdk_core20.ProtocolMismatchError,
60
+ SECRET_NAME_RE: () => import_sdk_core16.SECRET_NAME_RE,
61
+ SQLAction: () => import_sdk_core14.SQLAction,
62
+ SQLService: () => import_sdk_core14.SQLService,
63
+ SecretsService: () => import_sdk_core16.SecretsService,
64
+ ServiceContext: () => import_sdk_core21.ServiceContext,
65
+ SessionExpiredError: () => import_sdk_core12.SessionExpiredError,
66
+ SharingService: () => import_sdk_core17.SharingService,
67
+ SilentNotificationHandler: () => import_sdk_core11.SilentNotificationHandler,
68
+ Space: () => import_sdk_core19.Space,
69
+ SpaceErrorCodes: () => import_sdk_core19.SpaceErrorCodes,
70
+ SpaceService: () => import_sdk_core19.SpaceService,
71
+ TinyCloud: () => import_sdk_core10.TinyCloud,
72
+ TinyCloudDebugLogger: () => import_sdk_core11.TinyCloudDebugLogger,
72
73
  TinyCloudNode: () => TinyCloudNode,
73
- UnsupportedFeatureError: () => import_sdk_core19.UnsupportedFeatureError,
74
- VAULT_PERMISSION_SERVICE: () => import_sdk_core11.VAULT_PERMISSION_SERVICE,
75
- VaultHeaders: () => import_sdk_core15.VaultHeaders,
76
- VaultPublicSpaceKVActions: () => import_sdk_core15.VaultPublicSpaceKVActions,
77
- VersionCheckError: () => import_sdk_core19.VersionCheckError,
74
+ UnsupportedFeatureError: () => import_sdk_core20.UnsupportedFeatureError,
75
+ UnsupportedSessionRestoreError: () => UnsupportedSessionRestoreError,
76
+ VAULT_PERMISSION_SERVICE: () => import_sdk_core12.VAULT_PERMISSION_SERVICE,
77
+ VaultHeaders: () => import_sdk_core16.VaultHeaders,
78
+ VaultPublicSpaceKVActions: () => import_sdk_core16.VaultPublicSpaceKVActions,
79
+ VersionCheckError: () => import_sdk_core20.VersionCheckError,
78
80
  WasmKeyProvider: () => WasmKeyProvider,
79
- addressStorageKey: () => import_sdk_core10.addressStorageKey,
80
- buildSpaceUri: () => import_sdk_core18.buildSpaceUri,
81
- canonicalizeAddress: () => import_sdk_core10.canonicalizeAddress,
82
- canonicalizeDid: () => import_sdk_core10.canonicalizeDid,
83
- canonicalizeDidUrl: () => import_sdk_core10.canonicalizeDidUrl,
84
- canonicalizeNetworkId: () => import_sdk_core10.canonicalizeNetworkId,
85
- canonicalizeSecretScope: () => import_sdk_core15.canonicalizeSecretScope,
86
- checkNodeInfo: () => import_sdk_core19.checkNodeInfo,
87
- clearTinyCloudDebugLogs: () => import_sdk_core10.clearTinyCloudDebugLogs,
88
- composeManifestRequest: () => import_sdk_core11.composeManifestRequest,
89
- createCapabilityKeyRegistry: () => import_sdk_core17.createCapabilityKeyRegistry,
81
+ addressStorageKey: () => import_sdk_core11.addressStorageKey,
82
+ buildSpaceUri: () => import_sdk_core19.buildSpaceUri,
83
+ canonicalizeAddress: () => import_sdk_core11.canonicalizeAddress,
84
+ canonicalizeDid: () => import_sdk_core11.canonicalizeDid,
85
+ canonicalizeDidUrl: () => import_sdk_core11.canonicalizeDidUrl,
86
+ canonicalizeNetworkId: () => import_sdk_core11.canonicalizeNetworkId,
87
+ canonicalizeSecretScope: () => import_sdk_core16.canonicalizeSecretScope,
88
+ checkNodeInfo: () => import_sdk_core20.checkNodeInfo,
89
+ clearTinyCloudDebugLogs: () => import_sdk_core11.clearTinyCloudDebugLogs,
90
+ composeManifestRequest: () => import_sdk_core12.composeManifestRequest,
91
+ createCapabilityKeyRegistry: () => import_sdk_core18.createCapabilityKeyRegistry,
90
92
  createOpenKeyCallbackSigningStrategy: () => import_sdk_core2.createOpenKeyCallbackSigningStrategy,
91
- createSharingService: () => import_sdk_core16.createSharingService,
92
- createSpaceService: () => import_sdk_core18.createSpaceService,
93
- createVaultCrypto: () => import_sdk_core15.createVaultCrypto,
93
+ createSharingService: () => import_sdk_core17.createSharingService,
94
+ createSpaceService: () => import_sdk_core19.createSpaceService,
95
+ createVaultCrypto: () => import_sdk_core16.createVaultCrypto,
94
96
  createWasmKeyProvider: () => createWasmKeyProvider,
95
97
  defaultSignStrategy: () => defaultSignStrategy,
96
- defaultSpaceCreationHandler: () => import_sdk_core10.defaultSpaceCreationHandler,
98
+ defaultSpaceCreationHandler: () => import_sdk_core11.defaultSpaceCreationHandler,
97
99
  deserializeDelegation: () => deserializeDelegation,
98
- didCacheKey: () => import_sdk_core10.didCacheKey,
99
- didEquals: () => import_sdk_core10.didEquals,
100
- disableTinyCloudDebug: () => import_sdk_core10.disableTinyCloudDebug,
101
- enableTinyCloudDebug: () => import_sdk_core10.enableTinyCloudDebug,
102
- expandActionShortNames: () => import_sdk_core11.expandActionShortNames,
103
- expandPermissionEntries: () => import_sdk_core11.expandPermissionEntries,
104
- expandPermissionEntry: () => import_sdk_core11.expandPermissionEntry,
105
- getTinyCloudDebugLogs: () => import_sdk_core10.getTinyCloudDebugLogs,
106
- installTinyCloudDebugGlobals: () => import_sdk_core10.installTinyCloudDebugGlobals,
107
- isCapabilitySubset: () => import_sdk_core11.isCapabilitySubset,
108
- isEvmAddress: () => import_sdk_core10.isEvmAddress,
109
- loadManifest: () => import_sdk_core11.loadManifest,
110
- makePkhSpaceId: () => import_sdk_core10.makePkhSpaceId,
111
- makePublicSpaceId: () => import_sdk_core18.makePublicSpaceId,
112
- parseCanonicalNetworkId: () => import_sdk_core10.parseCanonicalNetworkId,
113
- parseExpiry: () => import_sdk_core11.parseExpiry,
114
- parsePkhDid: () => import_sdk_core10.parsePkhDid,
115
- parseSpaceUri: () => import_sdk_core18.parseSpaceUri,
116
- pkhDid: () => import_sdk_core10.pkhDid,
117
- principalDid: () => import_sdk_core10.principalDid,
118
- principalDidEquals: () => import_sdk_core10.principalDidEquals,
119
- resolveManifest: () => import_sdk_core11.resolveManifest,
120
- resolveSecretListPrefix: () => import_sdk_core15.resolveSecretListPrefix,
121
- resolveSecretPath: () => import_sdk_core15.resolveSecretPath,
122
- resourceCapabilitiesToSpaceAbilitiesMap: () => import_sdk_core11.resourceCapabilitiesToSpaceAbilitiesMap,
100
+ didCacheKey: () => import_sdk_core11.didCacheKey,
101
+ didEquals: () => import_sdk_core11.didEquals,
102
+ disableTinyCloudDebug: () => import_sdk_core11.disableTinyCloudDebug,
103
+ enableTinyCloudDebug: () => import_sdk_core11.enableTinyCloudDebug,
104
+ expandActionShortNames: () => import_sdk_core12.expandActionShortNames,
105
+ expandPermissionEntries: () => import_sdk_core12.expandPermissionEntries,
106
+ expandPermissionEntry: () => import_sdk_core12.expandPermissionEntry,
107
+ getTinyCloudDebugLogs: () => import_sdk_core11.getTinyCloudDebugLogs,
108
+ installTinyCloudDebugGlobals: () => import_sdk_core11.installTinyCloudDebugGlobals,
109
+ isCapabilitySubset: () => import_sdk_core12.isCapabilitySubset,
110
+ isEvmAddress: () => import_sdk_core11.isEvmAddress,
111
+ loadManifest: () => import_sdk_core12.loadManifest,
112
+ makePkhSpaceId: () => import_sdk_core11.makePkhSpaceId,
113
+ makePublicSpaceId: () => import_sdk_core19.makePublicSpaceId,
114
+ parseCanonicalNetworkId: () => import_sdk_core11.parseCanonicalNetworkId,
115
+ parseExpiry: () => import_sdk_core12.parseExpiry,
116
+ parsePkhDid: () => import_sdk_core11.parsePkhDid,
117
+ parseSpaceUri: () => import_sdk_core19.parseSpaceUri,
118
+ pkhDid: () => import_sdk_core11.pkhDid,
119
+ principalDid: () => import_sdk_core11.principalDid,
120
+ principalDidEquals: () => import_sdk_core11.principalDidEquals,
121
+ resolveManifest: () => import_sdk_core12.resolveManifest,
122
+ resolveSecretListPrefix: () => import_sdk_core16.resolveSecretListPrefix,
123
+ resolveSecretPath: () => import_sdk_core16.resolveSecretPath,
124
+ resourceCapabilitiesToSpaceAbilitiesMap: () => import_sdk_core12.resourceCapabilitiesToSpaceAbilitiesMap,
123
125
  serializeDelegation: () => serializeDelegation,
124
- tinyCloudDebugLogger: () => import_sdk_core10.tinyCloudDebugLogger,
125
- validateManifest: () => import_sdk_core11.validateManifest
126
+ tinyCloudDebugLogger: () => import_sdk_core11.tinyCloudDebugLogger,
127
+ validateManifest: () => import_sdk_core12.validateManifest
126
128
  });
127
129
  module.exports = __toCommonJS(core_exports);
128
- var import_sdk_core9 = require("@tinycloud/sdk-core");
129
130
  var import_sdk_core10 = require("@tinycloud/sdk-core");
131
+ var import_sdk_core11 = require("@tinycloud/sdk-core");
130
132
 
131
133
  // src/storage/MemorySessionStorage.ts
132
134
  var MemorySessionStorage = class {
@@ -467,6 +469,30 @@ var NodeUserAuthorization = class {
467
469
  this.tinycloudHosts = [...hosts];
468
470
  }
469
471
  }
472
+ /** @internal Atomically adopt the state staged by TinyCloudNode.restoreSession. */
473
+ installRestoredSession(sessionManager, session, hosts) {
474
+ this.sessionManager = sessionManager;
475
+ this.tinycloudHosts = hosts && hosts.length > 0 ? [...hosts] : void 0;
476
+ this._lastActivationSkippedSpaceIds = [];
477
+ if (!session) {
478
+ this._session = void 0;
479
+ this._tinyCloudSession = void 0;
480
+ this._address = void 0;
481
+ this._chainId = void 0;
482
+ return;
483
+ }
484
+ this._tinyCloudSession = { ...session };
485
+ this._address = session.address;
486
+ this._chainId = session.chainId;
487
+ this._session = {
488
+ address: session.address,
489
+ walletAddress: session.address,
490
+ chainId: session.chainId,
491
+ sessionKey: session.sessionKey,
492
+ siwe: session.siwe,
493
+ signature: session.signature
494
+ };
495
+ }
470
496
  /**
471
497
  * Ensure `tinycloudHosts` are resolved before a host-needing call.
472
498
  *
@@ -1239,6 +1265,7 @@ var NodeUserAuthorization = class {
1239
1265
 
1240
1266
  // src/TinyCloudNode.ts
1241
1267
  var import_sdk_core8 = require("@tinycloud/sdk-core");
1268
+ var import_sdk_services = require("@tinycloud/sdk-services");
1242
1269
 
1243
1270
  // src/account/AccountService.ts
1244
1271
  var import_sdk_core4 = require("@tinycloud/sdk-core");
@@ -1661,6 +1688,9 @@ var DEFAULT_ENCRYPTION_NETWORK_NAME = "default";
1661
1688
  var NETWORK_CREATE_ACTION2 = import_sdk_core8.ENCRYPTION.NETWORK_CREATE;
1662
1689
  var DECRYPT_ACTION2 = import_sdk_core8.ENCRYPTION.DECRYPT;
1663
1690
  var NETWORK_ADMIN_TYPE = "tinycloud.encryption.network-admin/v1";
1691
+ function isSecretPayload(value) {
1692
+ return typeof value === "object" && value !== null && "value" in value && typeof value.value === "string" && "createdAt" in value && typeof value.createdAt === "string" && "updatedAt" in value && typeof value.updatedAt === "string";
1693
+ }
1664
1694
  var ROOT_DELEGATION_ACTIONS = [
1665
1695
  import_sdk_core8.KV.PUT,
1666
1696
  import_sdk_core8.KV.GET,
@@ -1715,6 +1745,141 @@ function didPrincipalMatches2(actual, expected) {
1715
1745
  return actual === expected;
1716
1746
  }
1717
1747
  }
1748
+ function clonePersistedSessionJwk(jwk) {
1749
+ if (jwk === null || typeof jwk !== "object" || Array.isArray(jwk)) {
1750
+ throw new Error("Persisted session has an invalid private Ed25519 session key.");
1751
+ }
1752
+ try {
1753
+ const serialized = JSON.stringify(jwk);
1754
+ const cloned = serialized === void 0 ? void 0 : JSON.parse(serialized);
1755
+ if (cloned === null || typeof cloned !== "object" || Array.isArray(cloned)) {
1756
+ throw new Error("invalid JWK object");
1757
+ }
1758
+ return cloned;
1759
+ } catch {
1760
+ throw new Error("Persisted session has an invalid private Ed25519 session key.");
1761
+ }
1762
+ }
1763
+ function cloneRecapCaveats(caveats) {
1764
+ if (!caveats) return [];
1765
+ const cloneJson = (value) => {
1766
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
1767
+ return value;
1768
+ }
1769
+ if (typeof value === "number") {
1770
+ if (Number.isFinite(value)) return value;
1771
+ throw new Error("ReCap caveats must contain only JSON values.");
1772
+ }
1773
+ if (Array.isArray(value)) return value.map(cloneJson);
1774
+ if (typeof value === "object") {
1775
+ const prototype = Object.getPrototypeOf(value);
1776
+ if (prototype !== Object.prototype && prototype !== null) {
1777
+ throw new Error("ReCap caveats must contain only JSON values.");
1778
+ }
1779
+ const copy = /* @__PURE__ */ Object.create(null);
1780
+ for (const [key, nested] of Object.entries(value)) {
1781
+ copy[key] = cloneJson(nested);
1782
+ }
1783
+ return copy;
1784
+ }
1785
+ throw new Error("ReCap caveats must contain only JSON values.");
1786
+ };
1787
+ try {
1788
+ return JSON.parse(JSON.stringify(caveats.map(cloneJson)));
1789
+ } catch {
1790
+ throw new Error("Verified persisted session ReCap contains invalid caveats.");
1791
+ }
1792
+ }
1793
+ var ServiceGraphLifetime = class {
1794
+ constructor(invokeFn, invokeAnyFn, fetchFn) {
1795
+ this.invokeFn = invokeFn;
1796
+ this.invokeAnyFn = invokeAnyFn;
1797
+ this.fetchFn = fetchFn;
1798
+ this.abortController = new AbortController();
1799
+ this.contexts = /* @__PURE__ */ new Set();
1800
+ this.retired = false;
1801
+ this.invoke = (session, service, path, action, facts) => {
1802
+ this.assertActive();
1803
+ return this.invokeFn(session, service, path, action, facts);
1804
+ };
1805
+ this.invokeAny = (session, entries, facts) => {
1806
+ this.assertActive();
1807
+ return this.invokeAnyFn(session, entries, facts);
1808
+ };
1809
+ this.fetch = (url, init) => {
1810
+ this.assertActive();
1811
+ return this.fetchFn(url, {
1812
+ ...init,
1813
+ signal: this.combineSignals(init?.signal)
1814
+ });
1815
+ };
1816
+ }
1817
+ track(context) {
1818
+ this.assertActive();
1819
+ this.contexts.add(context);
1820
+ return context;
1821
+ }
1822
+ retire() {
1823
+ if (this.retired) return;
1824
+ this.retired = true;
1825
+ this.abortController.abort();
1826
+ try {
1827
+ for (const context of this.contexts) {
1828
+ try {
1829
+ const retire = context.retire;
1830
+ if (retire) retire.call(context);
1831
+ else context.abort();
1832
+ } catch (error) {
1833
+ console.error("Error retiring service graph context:", error);
1834
+ }
1835
+ }
1836
+ } finally {
1837
+ this.contexts.clear();
1838
+ }
1839
+ }
1840
+ assertActive() {
1841
+ if (this.retired) {
1842
+ throw new Error("Service graph has been retired by session replacement.");
1843
+ }
1844
+ }
1845
+ combineSignals(signal) {
1846
+ if (!signal) return this.abortController.signal;
1847
+ const combined = new AbortController();
1848
+ const abort = () => combined.abort();
1849
+ if (signal.aborted || this.abortController.signal.aborted) {
1850
+ abort();
1851
+ } else {
1852
+ signal.addEventListener("abort", abort, { once: true });
1853
+ this.abortController.signal.addEventListener("abort", abort, { once: true });
1854
+ }
1855
+ return combined.signal;
1856
+ }
1857
+ };
1858
+ var UnsupportedSessionRestoreError = class extends Error {
1859
+ constructor(reason = "it cannot replace and enumerate every live session signer") {
1860
+ super(`Persisted session restore is unsupported by this WASM binding because ${reason}.`);
1861
+ this.code = "RESTORE_SESSION_KEY_REPLACEMENT_UNSUPPORTED";
1862
+ this.name = "UnsupportedSessionRestoreError";
1863
+ }
1864
+ };
1865
+ function canonicalRestoredVerificationMethod(canonicalVerificationMethod, persistedVerificationMethod) {
1866
+ if (typeof persistedVerificationMethod !== "string") return void 0;
1867
+ const principal = canonicalVerificationMethod.split("#", 1)[0];
1868
+ return persistedVerificationMethod === principal || persistedVerificationMethod === canonicalVerificationMethod ? canonicalVerificationMethod : void 0;
1869
+ }
1870
+ function persistedExpiry(value) {
1871
+ if (typeof value !== "string") {
1872
+ throw new Error("Persisted session without a SIWE expiration must include expiresAt.");
1873
+ }
1874
+ const expiry = new Date(value);
1875
+ if (!Number.isFinite(expiry.getTime()) || expiry.getTime() <= Date.now()) {
1876
+ throw new Error("Persisted session expiry is invalid or expired.");
1877
+ }
1878
+ return expiry;
1879
+ }
1880
+ function sameInstant(left, right) {
1881
+ return left.getTime() === right.getTime();
1882
+ }
1718
1883
  function sharingActionsToAbilities(path, actions) {
1719
1884
  var _a;
1720
1885
  const abilities = {};
@@ -1841,6 +2006,7 @@ var _TinyCloudNode = class _TinyCloudNode {
1841
2006
  this.auth = null;
1842
2007
  this.tc = null;
1843
2008
  this._chainId = 1;
2009
+ this._baseVaults = /* @__PURE__ */ new Map();
1844
2010
  this._baseSecrets = /* @__PURE__ */ new Map();
1845
2011
  this._secrets = /* @__PURE__ */ new Map();
1846
2012
  this.runtimePermissionGrants = [];
@@ -1860,26 +2026,55 @@ var _TinyCloudNode = class _TinyCloudNode {
1860
2026
  skipped: false
1861
2027
  };
1862
2028
  this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
1863
- return this.wasmBindings.invoke(
1864
- this.selectInvocationSession(session, service, path, action),
1865
- service,
2029
+ const operation = {
2030
+ spaceId: session.spaceId,
2031
+ service: this.invocationServiceName(service),
1866
2032
  path,
1867
- action,
1868
- facts
2033
+ action
2034
+ };
2035
+ const grant = this.findGrantForOperation(operation);
2036
+ const grantedOperation = grant?.operations.find(
2037
+ (candidate) => this.operationCovers(candidate, operation)
1869
2038
  );
2039
+ const invocationSession = !grant || grant.provenance === "primary" ? session : grant.session;
2040
+ if ((grantedOperation?.caveats?.length ?? 0) > 0) {
2041
+ if (!this.wasmBindings.invokeAny) {
2042
+ throw new Error("WASM binding does not support caveat-preserving invocation");
2043
+ }
2044
+ return this.wasmBindings.invokeAny(invocationSession, [{
2045
+ spaceId: invocationSession.spaceId,
2046
+ service,
2047
+ path,
2048
+ action,
2049
+ caveats: cloneRecapCaveats(grantedOperation.caveats)
2050
+ }], facts);
2051
+ }
2052
+ return this.wasmBindings.invoke(invocationSession, service, path, action, facts);
1870
2053
  };
1871
2054
  this.invokeAnyWithRuntimePermissions = (session, entries, facts) => {
1872
2055
  if (!this.wasmBindings.invokeAny) {
1873
2056
  throw new Error("WASM binding does not support invokeAny");
1874
2057
  }
1875
- const grant = this.findGrantForOperations(
1876
- entries.flatMap((entry) => {
1877
- const operation = this.operationFromInvokeAnyEntry(entry);
1878
- return operation ? [operation] : [];
1879
- })
1880
- );
2058
+ const operations = entries.flatMap((entry) => {
2059
+ const operation = this.operationFromInvokeAnyEntry(entry);
2060
+ return operation ? [operation] : [];
2061
+ });
2062
+ const grant = this.findGrantForOperations(operations);
1881
2063
  const invocationSession = !grant || grant.provenance === "primary" ? session : grant.session;
1882
- return this.wasmBindings.invokeAny(invocationSession, entries, facts);
2064
+ const caveatPreservingEntries = grant ? entries.map((entry) => {
2065
+ const requested = this.operationFromInvokeAnyEntry(entry);
2066
+ const granted = requested && grant.operations.find(
2067
+ (candidate) => this.operationCovers(candidate, requested)
2068
+ );
2069
+ if (!granted?.caveats?.length) {
2070
+ return entry;
2071
+ }
2072
+ if (entry.caveats !== void 0 && !(0, import_sdk_core8.recapCaveatsEqual)(entry.caveats, granted.caveats)) {
2073
+ throw new Error("Invocation caveats do not match signed ReCap authority.");
2074
+ }
2075
+ return { ...entry, caveats: cloneRecapCaveats(granted.caveats) };
2076
+ }) : entries;
2077
+ return this.wasmBindings.invokeAny(invocationSession, caveatPreservingEntries, facts);
1883
2078
  };
1884
2079
  this.explicitHost = config.host;
1885
2080
  this.config = {
@@ -1908,16 +2103,19 @@ var _TinyCloudNode = class _TinyCloudNode {
1908
2103
  throw new Error("Failed to get session key JWK");
1909
2104
  }
1910
2105
  this.sessionKeyJwk = JSON.parse(jwkStr);
2106
+ this._serviceGraph = this.createServiceGraphLifetime();
1911
2107
  this._capabilityRegistry = new import_sdk_core8.CapabilityKeyRegistry();
1912
2108
  this._keyProvider = new WasmKeyProvider({
1913
2109
  sessionManager: this.sessionManager
1914
2110
  });
1915
2111
  this.notificationHandler = config.notificationHandler ?? new import_sdk_core8.SilentNotificationHandler();
2112
+ const receiveOnlyGraph = this._serviceGraph;
1916
2113
  this._sharingService = new import_sdk_core8.SharingService({
1917
2114
  hosts: [this.config.host],
1918
2115
  // session: undefined - not needed for receive()
1919
- invoke: this.invokeWithRuntimePermissions,
1920
- fetch: globalThis.fetch.bind(globalThis),
2116
+ invoke: receiveOnlyGraph.invoke,
2117
+ fetch: receiveOnlyGraph.fetch,
2118
+ assertActive: () => receiveOnlyGraph.assertActive(),
1921
2119
  keyProvider: this._keyProvider,
1922
2120
  registry: this._capabilityRegistry,
1923
2121
  createDelegationWasm: (params) => this.createDelegationWrapper(params),
@@ -1929,12 +2127,12 @@ var _TinyCloudNode = class _TinyCloudNode {
1929
2127
  createKVService: (config2) => {
1930
2128
  const prefix = config2.pathPrefix?.replace(/\/$/, "");
1931
2129
  const kvService = new import_sdk_core8.KVService({ prefix });
1932
- const kvContext = new import_sdk_core8.ServiceContext({
2130
+ const kvContext = receiveOnlyGraph.track(new import_sdk_core8.ServiceContext({
1933
2131
  invoke: config2.invoke,
1934
2132
  fetch: config2.fetch ?? globalThis.fetch.bind(globalThis),
1935
2133
  hosts: config2.hosts,
1936
2134
  telemetry: this.config.telemetry
1937
- });
2135
+ }));
1938
2136
  kvContext.setSession(config2.session);
1939
2137
  kvService.initialize(kvContext);
1940
2138
  return kvService;
@@ -2008,6 +2206,16 @@ var _TinyCloudNode = class _TinyCloudNode {
2008
2206
  shouldUseBootstrapSignInRequest(config) {
2009
2207
  return config.autoBootstrapAccount !== false && config.manifest === void 0 && config.capabilityRequest === void 0 && (config.prefix ?? "default") === "default" && isOpenKeyAutoSignStrategy(config.signStrategy);
2010
2208
  }
2209
+ createServiceGraphLifetime() {
2210
+ return new ServiceGraphLifetime(
2211
+ this.invokeWithRuntimePermissions,
2212
+ this.invokeAnyWithRuntimePermissions,
2213
+ // Resolve fetch at request time. This keeps the graph-owned lifetime
2214
+ // signal while preserving the SDK's existing injectable global fetch
2215
+ // behavior (including adapters that install it after construction).
2216
+ (url, init) => globalThis.fetch(url, init)
2217
+ );
2218
+ }
2011
2219
  syncResolvedHostFromAuth() {
2012
2220
  const host = this.auth?.hosts[0];
2013
2221
  if (host) {
@@ -2077,6 +2285,27 @@ var _TinyCloudNode = class _TinyCloudNode {
2077
2285
  get sessionDid() {
2078
2286
  return this.sessionManager.getDID(this.sessionKeyId);
2079
2287
  }
2288
+ /**
2289
+ * Return the current session's signed ReCap capabilities after the session
2290
+ * has been authenticated or restored. This is intentionally distinct from
2291
+ * installed runtime delegations: it reports base-session authority only.
2292
+ *
2293
+ * Invalid or unparseable signed ReCap material throws so callers fail closed
2294
+ * rather than treating a malformed session as unrestricted authority.
2295
+ */
2296
+ getVerifiedSessionCapabilities() {
2297
+ const session = this.currentTinyCloudSession();
2298
+ if (!session || !session.siwe) return [];
2299
+ return (0, import_sdk_core8.parseRecapCapabilities)(
2300
+ (siwe) => this.parseRecapWithCaveats(siwe),
2301
+ session.siwe
2302
+ ).map((entry) => entry.service === "tinycloud.encryption" ? {
2303
+ service: entry.service,
2304
+ path: entry.path,
2305
+ actions: [...entry.actions],
2306
+ ...entry.caveats === void 0 ? {} : { caveats: entry.caveats }
2307
+ } : entry);
2308
+ }
2080
2309
  /**
2081
2310
  * Get the Ethereum address for this user.
2082
2311
  */
@@ -2166,6 +2395,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2166
2395
  this._hooks = void 0;
2167
2396
  this._vault = void 0;
2168
2397
  this._encryption = void 0;
2398
+ this._baseVaults.clear();
2169
2399
  this._baseSecrets.clear();
2170
2400
  this._secrets.clear();
2171
2401
  this._spaceService = void 0;
@@ -2173,6 +2403,10 @@ var _TinyCloudNode = class _TinyCloudNode {
2173
2403
  this.runtimePermissionGrants = [];
2174
2404
  await this.tc.signIn(options);
2175
2405
  this.syncResolvedHostFromAuth();
2406
+ const oldGraph = this._serviceGraph;
2407
+ this._serviceGraph = this.createServiceGraphLifetime();
2408
+ oldGraph.retire();
2409
+ this.tc.retireServices();
2176
2410
  const signedInSession = this.currentTinyCloudSession();
2177
2411
  if (signedInSession) {
2178
2412
  this.sessionKeyId = signedInSession.sessionKey;
@@ -2457,7 +2691,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2457
2691
  }
2458
2692
  let operations = [];
2459
2693
  try {
2460
- const entries = this.wasmBindings.parseRecapFromSiwe(siwe);
2694
+ const entries = this.parseRecapWithCaveats(siwe);
2461
2695
  if (Array.isArray(entries)) {
2462
2696
  operations = entries.flatMap((entry) => {
2463
2697
  const service = this.invocationServiceName(entry.service);
@@ -2465,7 +2699,8 @@ var _TinyCloudNode = class _TinyCloudNode {
2465
2699
  ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
2466
2700
  service,
2467
2701
  path: entry.path,
2468
- action
2702
+ action,
2703
+ caveats: cloneRecapCaveats(entry.caveats)
2469
2704
  }));
2470
2705
  });
2471
2706
  }
@@ -2818,86 +3053,441 @@ var _TinyCloudNode = class _TinyCloudNode {
2818
3053
  */
2819
3054
  async restoreSession(sessionData) {
2820
3055
  await this.wasmBindings.ensureInitialized?.();
2821
- this._kv = void 0;
2822
- this._sql = void 0;
2823
- this._duckdb = void 0;
2824
- this._hooks = void 0;
2825
- this._vault = void 0;
2826
- this._encryption = void 0;
2827
- this._baseSecrets.clear();
2828
- this._secrets.clear();
2829
- this._spaceService = void 0;
2830
- this._serviceContext = void 0;
2831
- this.runtimePermissionGrants = [];
2832
- const restoredAddress = sessionData.address ? (0, import_sdk_core8.canonicalizeAddress)(sessionData.address) : void 0;
2833
- if (restoredAddress) {
2834
- this._address = restoredAddress;
3056
+ const restoredJwk = clonePersistedSessionJwk(sessionData.jwk);
3057
+ if (sessionData.chainId !== void 0 && (!Number.isSafeInteger(sessionData.chainId) || sessionData.chainId <= 0)) {
3058
+ throw new Error("Persisted session chain ID must be a positive safe integer.");
3059
+ }
3060
+ const stagedManager = this.wasmBindings.createSessionManager();
3061
+ const stagedReplace = stagedManager.replaceSessionKey;
3062
+ const liveKeys = this.sessionManager.listSessionKeys;
3063
+ const stagedKeys = stagedManager.listSessionKeys;
3064
+ if (typeof stagedReplace !== "function" || typeof liveKeys !== "function" || typeof stagedKeys !== "function") {
3065
+ throw new UnsupportedSessionRestoreError();
3066
+ }
3067
+ let stagedKeyId;
3068
+ let stagedJwk;
3069
+ let canonicalVerificationMethod;
3070
+ try {
3071
+ const keyIds = liveKeys.call(this.sessionManager);
3072
+ if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
3073
+ throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
3074
+ }
3075
+ for (const keyId of keyIds) {
3076
+ if (keyId === this.sessionKeyId) continue;
3077
+ const jwk = this.sessionManager.jwk(keyId);
3078
+ if (!jwk) throw new Error("missing live session key");
3079
+ stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
3080
+ }
3081
+ stagedKeyId = stagedReplace.call(stagedManager, restoredJwk, this.sessionKeyId);
3082
+ const stagedJwkJson = stagedManager.jwk(stagedKeyId);
3083
+ if (!stagedJwkJson) throw new Error("missing restored session key");
3084
+ stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
3085
+ canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
3086
+ } catch (error) {
3087
+ if (error instanceof UnsupportedSessionRestoreError) throw error;
3088
+ throw new Error("Persisted session has an invalid private Ed25519 session key.");
2835
3089
  }
2836
- if (sessionData.chainId) {
2837
- this._chainId = sessionData.chainId;
3090
+ const restoredVerificationMethod = canonicalRestoredVerificationMethod(
3091
+ canonicalVerificationMethod,
3092
+ sessionData.verificationMethod
3093
+ );
3094
+ if (!restoredVerificationMethod) {
3095
+ throw new Error(
3096
+ "Persisted session verification method does not match its private Ed25519 session key."
3097
+ );
3098
+ }
3099
+ const proofValues = [
3100
+ sessionData.siwe,
3101
+ sessionData.signature,
3102
+ sessionData.address,
3103
+ sessionData.chainId
3104
+ ];
3105
+ const hasPersistedProof = proofValues.every((value) => value !== void 0);
3106
+ if (!hasPersistedProof && (proofValues.some((value) => value !== void 0) || sessionData.expiresAt !== void 0)) {
3107
+ throw new Error("Persisted session authority metadata is incomplete.");
3108
+ }
3109
+ const restoredAddress = hasPersistedProof ? (0, import_sdk_core8.canonicalizeAddress)(sessionData.address) : void 0;
3110
+ let stagedSessionExpiry = /* @__PURE__ */ new Date(0);
3111
+ let stagedRecap = [];
3112
+ if (hasPersistedProof) {
3113
+ if (typeof this.wasmBindings.validatePersistedSession !== "function") {
3114
+ throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
3115
+ }
3116
+ const verified = this.wasmBindings.validatePersistedSession({
3117
+ delegationHeader: sessionData.delegationHeader,
3118
+ delegationCid: sessionData.delegationCid,
3119
+ spaceId: sessionData.spaceId,
3120
+ jwk: stagedJwk,
3121
+ address: restoredAddress,
3122
+ chainId: sessionData.chainId,
3123
+ siwe: sessionData.siwe,
3124
+ signature: sessionData.signature
3125
+ });
3126
+ const exactRecap = verified.verifiedRecap;
3127
+ if (!Array.isArray(exactRecap) || !exactRecap.every(
3128
+ (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(
3129
+ (caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
3130
+ )
3131
+ )) {
3132
+ throw new UnsupportedSessionRestoreError(
3133
+ "it cannot reconstruct caveat-preserving persisted ReCap authority"
3134
+ );
3135
+ }
3136
+ stagedRecap = exactRecap.map((entry) => ({
3137
+ service: entry.service,
3138
+ space: entry.space,
3139
+ path: entry.path,
3140
+ actions: [...entry.actions],
3141
+ caveats: cloneRecapCaveats(entry.caveats)
3142
+ }));
3143
+ const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
3144
+ const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
3145
+ if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
3146
+ throw new Error("Persisted session expiry does not match its signed SIWE authority.");
3147
+ }
3148
+ stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
2838
3149
  }
2839
3150
  const resolvedHost = await this.resolveRestoredHost(
2840
3151
  sessionData.tinycloudHosts,
2841
3152
  restoredAddress,
2842
3153
  sessionData.chainId
2843
3154
  );
2844
- if (resolvedHost) {
2845
- this.config.host = resolvedHost;
2846
- }
2847
- this._serviceContext = new import_sdk_core8.ServiceContext({
2848
- invoke: this.invokeWithRuntimePermissions,
2849
- invokeAny: this.invokeAnyWithRuntimePermissions,
2850
- fetch: globalThis.fetch.bind(globalThis),
2851
- hosts: [this.config.host],
2852
- telemetry: this.config.telemetry
2853
- });
2854
- this._kv = new import_sdk_core8.KVService({});
2855
- this._kv.initialize(this._serviceContext);
2856
- this._serviceContext.registerService("kv", this._kv);
2857
- this._sql = new import_sdk_core8.SQLService({});
2858
- this._sql.initialize(this._serviceContext);
2859
- this._serviceContext.registerService("sql", this._sql);
2860
- this._duckdb = new import_sdk_core8.DuckDbService({});
2861
- this._duckdb.initialize(this._serviceContext);
2862
- this._serviceContext.registerService("duckdb", this._duckdb);
2863
- this._hooks = new import_sdk_core8.HooksService({});
2864
- this._hooks.initialize(this._serviceContext);
2865
- this._serviceContext.registerService("hooks", this._hooks);
3155
+ const stagedHost = resolvedHost ?? this.config.host;
3156
+ const stagedAddress = restoredAddress;
3157
+ const stagedChainId = sessionData.chainId ?? 1;
3158
+ const stagedNodeDid = stagedAddress ? (0, import_sdk_core8.pkhDid)(stagedAddress, stagedChainId) : canonicalVerificationMethod;
2866
3159
  const serviceSession = {
2867
3160
  delegationHeader: sessionData.delegationHeader,
2868
3161
  delegationCid: sessionData.delegationCid,
2869
3162
  spaceId: sessionData.spaceId,
2870
- verificationMethod: sessionData.verificationMethod,
2871
- jwk: sessionData.jwk
3163
+ verificationMethod: restoredVerificationMethod,
3164
+ jwk: stagedJwk
2872
3165
  };
2873
- this._serviceContext.setSession(serviceSession);
2874
- this._vault = this.createVaultService(sessionData.spaceId, this._kv);
2875
- this._vault.initialize(this._serviceContext);
2876
- this._serviceContext.registerService("vault", this._vault);
2877
- this.initializeV2Services(serviceSession);
2878
- if (sessionData.siwe && restoredAddress && sessionData.chainId) {
2879
- const tcSession = {
2880
- address: restoredAddress,
2881
- chainId: sessionData.chainId,
2882
- sessionKey: JSON.stringify(sessionData.jwk),
2883
- spaceId: sessionData.spaceId,
2884
- delegationCid: sessionData.delegationCid,
2885
- delegationHeader: sessionData.delegationHeader,
2886
- verificationMethod: sessionData.verificationMethod,
2887
- jwk: sessionData.jwk,
2888
- siwe: sessionData.siwe,
2889
- signature: sessionData.signature ?? ""
2890
- };
2891
- if (this.auth) {
2892
- this.auth.setRestoredTinyCloudSession(
2893
- tcSession,
2894
- this.config.host ? [this.config.host] : void 0
2895
- );
2896
- } else {
2897
- this._restoredTcSession = tcSession;
3166
+ const stagedTcSession = hasPersistedProof ? {
3167
+ address: restoredAddress,
3168
+ chainId: sessionData.chainId,
3169
+ sessionKey: JSON.stringify(stagedJwk),
3170
+ spaceId: sessionData.spaceId,
3171
+ spaces: sessionData.spaces,
3172
+ delegationCid: sessionData.delegationCid,
3173
+ delegationHeader: sessionData.delegationHeader,
3174
+ verificationMethod: restoredVerificationMethod,
3175
+ jwk: stagedJwk,
3176
+ siwe: sessionData.siwe,
3177
+ signature: sessionData.signature
3178
+ } : void 0;
3179
+ const stagedPrimary = this.stagePrimarySessionState(
3180
+ stagedTcSession,
3181
+ stagedNodeDid,
3182
+ stagedHost,
3183
+ stagedSessionExpiry,
3184
+ stagedRecap
3185
+ );
3186
+ const stagedGraph = this.stageRestoredServiceGraph({
3187
+ host: stagedHost,
3188
+ manager: stagedManager,
3189
+ serviceSession,
3190
+ verificationMethod: canonicalVerificationMethod,
3191
+ nodeDid: stagedNodeDid,
3192
+ address: stagedAddress,
3193
+ chainId: stagedChainId,
3194
+ tinyCloudSession: stagedTcSession,
3195
+ sessionExpiry: stagedSessionExpiry,
3196
+ recap: stagedRecap
3197
+ });
3198
+ const oldGraph = this._serviceGraph;
3199
+ const oldCore = this.tc;
3200
+ this.sessionManager = stagedManager;
3201
+ this.sessionKeyId = stagedKeyId;
3202
+ this.sessionKeyJwk = stagedJwk;
3203
+ if (resolvedHost) this.config.host = resolvedHost;
3204
+ this._address = stagedAddress;
3205
+ this._chainId = stagedChainId;
3206
+ this._serviceContext = stagedGraph.serviceContext;
3207
+ this._kv = stagedGraph.kv;
3208
+ this._sql = stagedGraph.sql;
3209
+ this._duckdb = stagedGraph.duckdb;
3210
+ this._hooks = stagedGraph.hooks;
3211
+ this._vault = stagedGraph.vault;
3212
+ this._encryption = stagedGraph.encryption;
3213
+ this._capabilityRegistry = stagedGraph.capabilityRegistry;
3214
+ this._keyProvider = stagedGraph.keyProvider;
3215
+ this._sharingService = stagedGraph.sharingService;
3216
+ this._delegationManager = stagedGraph.delegationManager;
3217
+ this._spaceService = stagedGraph.spaceService;
3218
+ this._serviceGraph = stagedGraph.graph;
3219
+ this._baseSecrets = /* @__PURE__ */ new Map();
3220
+ this._secrets = /* @__PURE__ */ new Map();
3221
+ this._publicKV = void 0;
3222
+ this._account = void 0;
3223
+ this.tc = stagedGraph.core;
3224
+ this._recapOperationsCache = stagedPrimary.cache;
3225
+ this.runtimePermissionGrants = stagedPrimary.grants;
3226
+ if (this.auth) {
3227
+ this.auth.installRestoredSession(stagedManager, stagedTcSession, [stagedHost]);
3228
+ this._restoredTcSession = void 0;
3229
+ } else {
3230
+ this._restoredTcSession = stagedTcSession;
3231
+ }
3232
+ oldGraph.retire();
3233
+ oldCore?.retireServices?.();
3234
+ }
3235
+ stagePrimarySessionState(session, verificationMethod, host, sessionExpiry, recap) {
3236
+ if (!session?.siwe) return { cache: void 0, grants: [] };
3237
+ let operations = [];
3238
+ operations = recap.flatMap((entry) => {
3239
+ const service = this.invocationServiceName(entry.service);
3240
+ return entry.actions.map((action) => ({
3241
+ ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
3242
+ service,
3243
+ path: entry.path,
3244
+ action,
3245
+ caveats: cloneRecapCaveats(entry.caveats)
3246
+ }));
3247
+ });
3248
+ const cache = { siwe: session.siwe, operations };
3249
+ if (operations.length === 0) return { cache, grants: [] };
3250
+ const expiresAt = sessionExpiry;
3251
+ return {
3252
+ cache,
3253
+ grants: [{
3254
+ session: {
3255
+ delegationHeader: session.delegationHeader,
3256
+ delegationCid: session.delegationCid,
3257
+ spaceId: session.spaceId,
3258
+ verificationMethod: session.verificationMethod,
3259
+ jwk: session.jwk
3260
+ },
3261
+ delegation: {
3262
+ cid: session.delegationCid,
3263
+ delegationHeader: session.delegationHeader,
3264
+ delegateDID: session.verificationMethod,
3265
+ delegatorDID: verificationMethod,
3266
+ spaceId: session.spaceId,
3267
+ path: "",
3268
+ actions: [...new Set(operations.map((operation) => operation.action))],
3269
+ expiry: expiresAt,
3270
+ allowSubDelegation: true,
3271
+ ownerAddress: session.address,
3272
+ chainId: session.chainId,
3273
+ host
3274
+ },
3275
+ operations,
3276
+ expiresAt,
3277
+ provenance: "primary"
3278
+ }]
3279
+ };
3280
+ }
3281
+ stageRestoredServiceGraph(input) {
3282
+ const graph = this.createServiceGraphLifetime();
3283
+ const core = this.auth ? new import_sdk_core8.TinyCloud(this.auth, {
3284
+ invokeAny: graph.invokeAny,
3285
+ telemetry: this.config.telemetry
3286
+ }) : null;
3287
+ if (core) {
3288
+ core.initializeServices(graph.invoke, [input.host], graph.fetch);
3289
+ const coreContext = graph.track(core.serviceContext);
3290
+ coreContext.setSession(input.serviceSession);
3291
+ }
3292
+ const serviceContext = graph.track(new import_sdk_core8.ServiceContext({
3293
+ invoke: graph.invoke,
3294
+ invokeAny: graph.invokeAny,
3295
+ fetch: graph.fetch,
3296
+ hosts: [input.host],
3297
+ telemetry: this.config.telemetry
3298
+ }));
3299
+ const kv = new import_sdk_core8.KVService({});
3300
+ kv.initialize(serviceContext);
3301
+ serviceContext.registerService("kv", kv);
3302
+ const sql = new import_sdk_core8.SQLService({});
3303
+ sql.initialize(serviceContext);
3304
+ serviceContext.registerService("sql", sql);
3305
+ const duckdb = new import_sdk_core8.DuckDbService({});
3306
+ duckdb.initialize(serviceContext);
3307
+ serviceContext.registerService("duckdb", duckdb);
3308
+ const hooks = new import_sdk_core8.HooksService({});
3309
+ hooks.initialize(serviceContext);
3310
+ serviceContext.registerService("hooks", hooks);
3311
+ serviceContext.setSession(input.serviceSession);
3312
+ const encryption = this.createEncryptionService({
3313
+ graph,
3314
+ host: input.host,
3315
+ session: input.serviceSession,
3316
+ did: input.nodeDid,
3317
+ address: input.address,
3318
+ chainId: input.chainId
3319
+ });
3320
+ encryption.initialize(serviceContext);
3321
+ serviceContext.registerService("encryption", encryption);
3322
+ const vault = this.createVaultService(input.serviceSession.spaceId, kv, encryption, {
3323
+ host: input.host,
3324
+ did: input.nodeDid,
3325
+ address: input.address,
3326
+ chainId: input.chainId,
3327
+ serviceGraph: graph
3328
+ });
3329
+ vault.initialize(serviceContext);
3330
+ serviceContext.registerService("vault", vault);
3331
+ const capabilityRegistry = new import_sdk_core8.CapabilityKeyRegistry();
3332
+ if (input.tinyCloudSession) {
3333
+ const session = input.tinyCloudSession;
3334
+ const delegations = input.recap.map((entry) => ({
3335
+ cid: session.delegationCid,
3336
+ delegateDID: session.verificationMethod,
3337
+ spaceId: entry.space,
3338
+ path: entry.path,
3339
+ actions: [...entry.actions],
3340
+ caveats: cloneRecapCaveats(entry.caveats),
3341
+ expiry: input.sessionExpiry,
3342
+ isRevoked: false,
3343
+ allowSubDelegation: true
3344
+ }));
3345
+ if (delegations.length > 0) {
3346
+ capabilityRegistry.registerKey({
3347
+ id: session.sessionKey,
3348
+ did: session.verificationMethod,
3349
+ type: "session",
3350
+ jwk: session.jwk,
3351
+ priority: 0
3352
+ }, delegations);
2898
3353
  }
2899
- this.registerPrimarySessionGrant(tcSession);
2900
3354
  }
3355
+ const delegationManager = new import_sdk_core8.DelegationManager({
3356
+ hosts: [input.host],
3357
+ session: input.serviceSession,
3358
+ invoke: graph.invoke,
3359
+ invokeAny: graph.invokeAny,
3360
+ fetch: graph.fetch
3361
+ });
3362
+ const keyProvider = new WasmKeyProvider({ sessionManager: input.manager });
3363
+ const sharingService = new import_sdk_core8.SharingService({
3364
+ hosts: [input.host],
3365
+ invoke: graph.invoke,
3366
+ fetch: graph.fetch,
3367
+ assertActive: () => graph.assertActive(),
3368
+ keyProvider,
3369
+ registry: capabilityRegistry,
3370
+ createDelegationWasm: (params) => this.createDelegationWrapper(params),
3371
+ computeCid: (data, codec) => {
3372
+ if (!this.wasmBindings.computeCid) throw new Error("computeCid is unavailable");
3373
+ return this.wasmBindings.computeCid(data, codec);
3374
+ },
3375
+ createKVService: (config) => {
3376
+ const service = new import_sdk_core8.KVService({ prefix: config.pathPrefix?.replace(/\/$/, "") });
3377
+ const context = graph.track(new import_sdk_core8.ServiceContext({
3378
+ invoke: config.invoke,
3379
+ fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
3380
+ hosts: config.hosts,
3381
+ telemetry: this.config.telemetry
3382
+ }));
3383
+ context.setSession(config.session);
3384
+ service.initialize(context);
3385
+ return service;
3386
+ }
3387
+ });
3388
+ sharingService.updateConfig({
3389
+ session: input.serviceSession,
3390
+ delegationManager,
3391
+ sessionExpiry: input.sessionExpiry,
3392
+ createDelegationWasm: (params) => {
3393
+ graph.assertActive();
3394
+ return this.createDelegationWrapper(params);
3395
+ },
3396
+ onRootDelegationNeeded: this.signer ? async (params) => {
3397
+ graph.assertActive();
3398
+ const delegation = await this.createRootDelegationForSharing(
3399
+ params,
3400
+ () => graph.assertActive()
3401
+ );
3402
+ graph.assertActive();
3403
+ return delegation;
3404
+ } : void 0
3405
+ });
3406
+ const spaceService = new import_sdk_core8.SpaceService({
3407
+ hosts: [input.host],
3408
+ session: input.serviceSession,
3409
+ invoke: graph.invoke,
3410
+ fetch: graph.fetch,
3411
+ capabilityRegistry,
3412
+ userDid: input.nodeDid,
3413
+ createKVService: (spaceId) => {
3414
+ graph.assertActive();
3415
+ const scopedKv = new import_sdk_core8.KVService({});
3416
+ const context = graph.track(new import_sdk_core8.ServiceContext({
3417
+ invoke: graph.invoke,
3418
+ invokeAny: graph.invokeAny,
3419
+ fetch: graph.fetch,
3420
+ hosts: [input.host],
3421
+ telemetry: this.config.telemetry
3422
+ }));
3423
+ context.setSession({ ...input.serviceSession, spaceId });
3424
+ scopedKv.initialize(context);
3425
+ return scopedKv;
3426
+ },
3427
+ createVaultService: (spaceId) => {
3428
+ graph.assertActive();
3429
+ const scopedKv = new import_sdk_core8.KVService({});
3430
+ const context = graph.track(new import_sdk_core8.ServiceContext({
3431
+ invoke: graph.invoke,
3432
+ invokeAny: graph.invokeAny,
3433
+ fetch: graph.fetch,
3434
+ hosts: [input.host],
3435
+ telemetry: this.config.telemetry
3436
+ }));
3437
+ context.setSession({ ...input.serviceSession, spaceId });
3438
+ scopedKv.initialize(context);
3439
+ const scopedVault = this.createVaultService(spaceId, scopedKv, encryption, {
3440
+ host: input.host,
3441
+ did: input.nodeDid,
3442
+ address: input.address,
3443
+ chainId: input.chainId,
3444
+ serviceGraph: graph
3445
+ });
3446
+ scopedVault.initialize(context);
3447
+ return scopedVault;
3448
+ },
3449
+ createSecretsService: (spaceId) => {
3450
+ graph.assertActive();
3451
+ return this.secretsForSpace(spaceId);
3452
+ },
3453
+ createDelegation: async (params) => {
3454
+ graph.assertActive();
3455
+ try {
3456
+ const portableDelegation = await this.createDelegation({
3457
+ delegateDID: params.delegateDID,
3458
+ path: params.path,
3459
+ actions: params.actions,
3460
+ disableSubDelegation: params.disableSubDelegation,
3461
+ expiryMs: params.expiry ? params.expiry.getTime() - Date.now() : void 0
3462
+ });
3463
+ return { ok: true, data: {
3464
+ cid: portableDelegation.cid,
3465
+ delegateDID: portableDelegation.delegateDID,
3466
+ delegatorDID: this.did,
3467
+ spaceId: portableDelegation.spaceId,
3468
+ path: portableDelegation.path,
3469
+ actions: portableDelegation.actions,
3470
+ expiry: portableDelegation.expiry,
3471
+ isRevoked: false,
3472
+ allowSubDelegation: !portableDelegation.disableSubDelegation,
3473
+ createdAt: /* @__PURE__ */ new Date(),
3474
+ authHeader: portableDelegation.delegationHeader.Authorization
3475
+ } };
3476
+ } catch (error) {
3477
+ return { ok: false, error: {
3478
+ code: "CREATION_FAILED",
3479
+ message: error instanceof Error ? error.message : String(error),
3480
+ service: "delegation"
3481
+ } };
3482
+ }
3483
+ },
3484
+ onSpaceRegistered: async (space) => {
3485
+ graph.assertActive();
3486
+ await this.account.spaces.register(space);
3487
+ }
3488
+ });
3489
+ spaceService.updateConfig({ sharingService });
3490
+ return { core, graph, serviceContext, kv, sql, duckdb, hooks, vault, encryption, capabilityRegistry, keyProvider, sharingService, delegationManager, spaceService };
2901
3491
  }
2902
3492
  /**
2903
3493
  * Resolve the host a restored session should target.
@@ -3059,14 +3649,19 @@ var _TinyCloudNode = class _TinyCloudNode {
3059
3649
  if (!session) {
3060
3650
  return;
3061
3651
  }
3062
- this.tc.initializeServices(this.invokeWithRuntimePermissions, [this.config.host]);
3063
- this._serviceContext = new import_sdk_core8.ServiceContext({
3064
- invoke: this.invokeWithRuntimePermissions,
3065
- invokeAny: this.invokeAnyWithRuntimePermissions,
3066
- fetch: globalThis.fetch.bind(globalThis),
3652
+ this.tc.initializeServices(
3653
+ this._serviceGraph.invoke,
3654
+ [this.config.host],
3655
+ this._serviceGraph.fetch
3656
+ );
3657
+ this._serviceGraph.track(this.tc.serviceContext);
3658
+ this._serviceContext = this._serviceGraph.track(new import_sdk_core8.ServiceContext({
3659
+ invoke: this._serviceGraph.invoke,
3660
+ invokeAny: this._serviceGraph.invokeAny,
3661
+ fetch: this._serviceGraph.fetch,
3067
3662
  hosts: [this.config.host],
3068
3663
  telemetry: this.config.telemetry
3069
- });
3664
+ }));
3070
3665
  this._kv = new import_sdk_core8.KVService({});
3071
3666
  this._kv.initialize(this._serviceContext);
3072
3667
  this._serviceContext.registerService("kv", this._kv);
@@ -3101,12 +3696,12 @@ var _TinyCloudNode = class _TinyCloudNode {
3101
3696
  createSpaceScopedKVService(spaceId) {
3102
3697
  const kvService = new import_sdk_core8.KVService({});
3103
3698
  if (this._serviceContext) {
3104
- const spaceScopedContext = new import_sdk_core8.ServiceContext({
3699
+ const spaceScopedContext = this._serviceGraph.track(new import_sdk_core8.ServiceContext({
3105
3700
  invoke: this._serviceContext.invoke,
3106
3701
  fetch: this._serviceContext.fetch,
3107
3702
  hosts: this._serviceContext.hosts,
3108
3703
  telemetry: this.config.telemetry
3109
- });
3704
+ }));
3110
3705
  const session = this._serviceContext.session;
3111
3706
  if (session) {
3112
3707
  spaceScopedContext.setSession({ ...session, spaceId });
@@ -3139,6 +3734,18 @@ var _TinyCloudNode = class _TinyCloudNode {
3139
3734
  }
3140
3735
  return session;
3141
3736
  }
3737
+ /**
3738
+ * Runtime-permission helpers can be used from an authenticated Node session
3739
+ * before its public ServiceContext is initialized. They still need the
3740
+ * current signed session, never an optional or stale authority object.
3741
+ */
3742
+ requireEncryptionSession() {
3743
+ const session = this._serviceContext?.session ?? this.auth?.tinyCloudSession;
3744
+ if (!session) {
3745
+ throw new Error("Not signed in. Call signIn() first.");
3746
+ }
3747
+ return session;
3748
+ }
3142
3749
  createEncryptionCrypto() {
3143
3750
  const wasm = this.wasmBindings;
3144
3751
  const columnEncrypt = (key, plaintext) => {
@@ -3194,14 +3801,15 @@ var _TinyCloudNode = class _TinyCloudNode {
3194
3801
  }
3195
3802
  return info.nodeId;
3196
3803
  }
3197
- async signRawNetworkAuthorization(input) {
3804
+ async signRawNetworkAuthorization(input, binding) {
3805
+ binding?.graph.assertActive();
3198
3806
  if (!this.wasmBindings.invokeAny) {
3199
3807
  throw new Error("WASM binding does not support raw-resource invokeAny");
3200
3808
  }
3201
3809
  if (!this.wasmBindings.computeCid) {
3202
3810
  throw new Error("WASM binding does not support invocation CID computation");
3203
3811
  }
3204
- const session = this.requireServiceSession();
3812
+ const session = binding?.session ?? this.requireEncryptionSession();
3205
3813
  const headers = this.invokeAnyWithRuntimePermissions(
3206
3814
  session,
3207
3815
  [
@@ -3220,6 +3828,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3220
3828
  input.targetNode,
3221
3829
  session.jwk
3222
3830
  );
3831
+ binding?.graph.assertActive();
3223
3832
  return {
3224
3833
  authorization: audienceBound,
3225
3834
  invocationCid: this.wasmBindings.computeCid(
@@ -3228,25 +3837,55 @@ var _TinyCloudNode = class _TinyCloudNode {
3228
3837
  )
3229
3838
  };
3230
3839
  }
3231
- createEncryptionService() {
3840
+ async fetchEncryptionNetworkAt(host, networkId, fetchFn) {
3841
+ const response = await fetchFn(
3842
+ `${host}/encryption/networks/${encodeURIComponent(networkId)}`
3843
+ );
3844
+ if (response.status === 404) return null;
3845
+ if (!response.ok) {
3846
+ throw new Error(
3847
+ `Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
3848
+ );
3849
+ }
3850
+ const body = await response.json();
3851
+ return "descriptor" in body && body.descriptor ? body.descriptor : body;
3852
+ }
3853
+ createEncryptionService(binding) {
3854
+ const graph = binding?.graph ?? this._serviceGraph;
3855
+ const host = binding?.host ?? this.config.host;
3856
+ const session = binding?.session ?? this.requireEncryptionSession();
3857
+ const did = binding?.did ?? this.did;
3858
+ const address = binding?.address ?? this._address;
3859
+ const chainId = binding?.chainId ?? this._chainId;
3232
3860
  const crypto = this.createEncryptionCrypto();
3233
3861
  const transport = {
3234
- postDecrypt: async ({ networkId, authorization, canonicalBody }) => {
3235
- const response = await fetch(
3236
- `${this.config.host}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
3862
+ postDecrypt: async ({ networkId, authorization, canonicalBody, signal }) => {
3863
+ graph.assertActive();
3864
+ const response = await graph.fetch(
3865
+ `${host}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
3237
3866
  {
3238
3867
  method: "POST",
3239
3868
  headers: {
3240
3869
  Authorization: authorization,
3241
3870
  "Content-Type": "application/json"
3242
3871
  },
3243
- body: canonicalBody
3872
+ body: canonicalBody,
3873
+ signal
3244
3874
  }
3245
3875
  );
3876
+ graph.assertActive();
3246
3877
  if (!response.ok) {
3247
- throw new Error(
3248
- `decrypt failed ${response.status}: ${await response.text()}`
3249
- );
3878
+ let permissionHint;
3879
+ if (response.status === 401 || response.status === 403) {
3880
+ try {
3881
+ const body = await response.json();
3882
+ const record = typeof body === "object" && body !== null ? body : void 0;
3883
+ const nested = typeof record?.error === "object" && record.error !== null ? record.error : void 0;
3884
+ permissionHint = (0, import_sdk_services.parsePermissionHint)(record?.permissionHint) ?? (0, import_sdk_services.parsePermissionHint)(nested?.permissionHint);
3885
+ } catch {
3886
+ }
3887
+ }
3888
+ throw new import_sdk_core8.DecryptTransportResponseError(response.status, permissionHint);
3250
3889
  }
3251
3890
  return await response.json();
3252
3891
  }
@@ -3255,12 +3894,14 @@ var _TinyCloudNode = class _TinyCloudNode {
3255
3894
  crypto,
3256
3895
  signer: {
3257
3896
  signDecryptInvocation: async (input) => {
3897
+ graph.assertActive();
3258
3898
  const signed = await this.signRawNetworkAuthorization({
3259
3899
  targetNode: input.targetNode,
3260
3900
  networkId: input.networkId,
3261
3901
  action: DECRYPT_ACTION2,
3262
3902
  facts: input.facts
3263
- });
3903
+ }, { graph, session });
3904
+ graph.assertActive();
3264
3905
  return {
3265
3906
  ...signed,
3266
3907
  canonicalBody: (0, import_sdk_core8.canonicalizeEncryptionJson)(
@@ -3271,25 +3912,35 @@ var _TinyCloudNode = class _TinyCloudNode {
3271
3912
  },
3272
3913
  transport,
3273
3914
  node: {
3274
- fetchByNetworkId: (networkId) => this.getEncryptionNetwork(networkId)
3915
+ fetchByNetworkId: async (networkId) => {
3916
+ graph.assertActive();
3917
+ const descriptor = await this.fetchEncryptionNetworkAt(host, networkId, graph.fetch);
3918
+ graph.assertActive();
3919
+ return descriptor;
3920
+ }
3275
3921
  },
3276
3922
  wellKnown: {
3277
3923
  fetchWellKnown: async (principal, discoveryKey) => {
3278
- if (!this._address || !didPrincipalMatches2(principal, this.did)) {
3279
- return null;
3280
- }
3281
- if (!this.config.host) {
3924
+ graph.assertActive();
3925
+ if (!address || !didPrincipalMatches2(principal, did)) {
3282
3926
  return null;
3283
3927
  }
3284
- const publicSpaceId = (0, import_sdk_core8.makePublicSpaceId)(this._address, this._chainId);
3285
- const result = await import_sdk_core8.TinyCloud.readPublicSpace(this.config.host, publicSpaceId, discoveryKey);
3286
- if (!result.ok) {
3928
+ const publicSpaceId = (0, import_sdk_core8.makePublicSpaceId)(address, chainId);
3929
+ const encodedKey = discoveryKey.split("/").map(encodeURIComponent).join("/");
3930
+ const response = await graph.fetch(
3931
+ `${host}/public/${encodeURIComponent(publicSpaceId)}/kv/${encodedKey}`,
3932
+ { method: "GET" }
3933
+ );
3934
+ graph.assertActive();
3935
+ if (!response.ok) {
3287
3936
  return null;
3288
3937
  }
3289
- const body = result.data;
3938
+ const body = await response.json();
3939
+ graph.assertActive();
3290
3940
  return "descriptor" in body && body.descriptor ? body.descriptor : body;
3291
3941
  }
3292
- }
3942
+ },
3943
+ assertActive: () => graph.assertActive()
3293
3944
  });
3294
3945
  }
3295
3946
  getEncryptionService() {
@@ -3303,8 +3954,9 @@ var _TinyCloudNode = class _TinyCloudNode {
3303
3954
  }
3304
3955
  return this._encryption;
3305
3956
  }
3306
- createVaultService(spaceId, kv) {
3957
+ createVaultService(spaceId, kv, encryptionService = this.getEncryptionService(), nodeContext) {
3307
3958
  const wasm = this.wasmBindings;
3959
+ const serviceGraph = nodeContext?.serviceGraph ?? this._serviceGraph;
3308
3960
  const vaultCrypto = (0, import_sdk_core8.createVaultCrypto)({
3309
3961
  vault_encrypt: wasm.vault_encrypt,
3310
3962
  vault_decrypt: wasm.vault_decrypt,
@@ -3315,12 +3967,17 @@ var _TinyCloudNode = class _TinyCloudNode {
3315
3967
  vault_sha256: wasm.vault_sha256
3316
3968
  });
3317
3969
  const self = this;
3970
+ const did = nodeContext?.did ?? this.did;
3971
+ const address = nodeContext?.address ?? this._address;
3972
+ const chainId = nodeContext?.chainId ?? this._chainId;
3973
+ const host = nodeContext?.host ?? this.config.host;
3974
+ const ownerDid = this.ownerDidFromSpaceId(spaceId) ?? did;
3318
3975
  return new import_sdk_core8.DataVaultService({
3319
3976
  spaceId,
3320
3977
  crypto: vaultCrypto,
3321
3978
  encryption: {
3322
- networkId: this.getEncryptionNetworkIdForSpace(spaceId),
3323
- service: this.getEncryptionService(),
3979
+ networkId: `urn:tinycloud:encryption:${ownerDid}:${DEFAULT_ENCRYPTION_NETWORK_NAME}`,
3980
+ service: encryptionService,
3324
3981
  decryptCapabilityProof: () => ({
3325
3982
  proofs: [this.requireServiceSession().delegationCid]
3326
3983
  })
@@ -3329,6 +3986,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3329
3986
  kv,
3330
3987
  ensurePublicSpace: async () => {
3331
3988
  try {
3989
+ serviceGraph.assertActive();
3332
3990
  await self.ensurePublicSpace();
3333
3991
  return { ok: true, data: void 0 };
3334
3992
  } catch (error) {
@@ -3336,14 +3994,15 @@ var _TinyCloudNode = class _TinyCloudNode {
3336
3994
  }
3337
3995
  },
3338
3996
  get publicKV() {
3997
+ serviceGraph.assertActive();
3339
3998
  return self._publicKV ?? self.tc.publicKV;
3340
3999
  },
3341
- readPublicSpace: (host, targetSpaceId, key) => import_sdk_core8.TinyCloud.readPublicSpace(host, targetSpaceId, key),
4000
+ readPublicSpace: (host2, targetSpaceId, key) => import_sdk_core8.TinyCloud.readPublicSpace(host2, targetSpaceId, key),
3342
4001
  makePublicSpaceId: import_sdk_core8.TinyCloud.makePublicSpaceId,
3343
- did: this.did,
3344
- address: this._address ?? "",
3345
- chainId: this._chainId,
3346
- hosts: [this.config.host]
4002
+ did,
4003
+ address: address ?? "",
4004
+ chainId,
4005
+ hosts: [host]
3347
4006
  }
3348
4007
  });
3349
4008
  }
@@ -3352,6 +4011,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3352
4011
  * @internal
3353
4012
  */
3354
4013
  initializeV2Services(serviceSession) {
4014
+ const graph = this._serviceGraph;
3355
4015
  this._capabilityRegistry = new import_sdk_core8.CapabilityKeyRegistry();
3356
4016
  const tcSession = this.auth?.tinyCloudSession;
3357
4017
  if (tcSession && this._address) {
@@ -3396,21 +4056,23 @@ var _TinyCloudNode = class _TinyCloudNode {
3396
4056
  hosts: [this.config.host],
3397
4057
  accountSpaceId: this.accountSpaceId,
3398
4058
  session: serviceSession,
3399
- invoke: this.invokeWithRuntimePermissions,
3400
- invokeAny: this.invokeAnyWithRuntimePermissions,
3401
- fetch: globalThis.fetch.bind(globalThis)
4059
+ invoke: graph.invoke,
4060
+ invokeAny: graph.invokeAny,
4061
+ fetch: graph.fetch
3402
4062
  });
3403
4063
  this._spaceService = new import_sdk_core8.SpaceService({
3404
4064
  hosts: [this.config.host],
3405
4065
  session: serviceSession,
3406
- invoke: this.wasmBindings.invoke,
3407
- fetch: globalThis.fetch.bind(globalThis),
4066
+ invoke: graph.invoke,
4067
+ fetch: graph.fetch,
3408
4068
  capabilityRegistry: this._capabilityRegistry,
3409
4069
  userDid: this.did,
3410
4070
  createKVService: (spaceId) => {
4071
+ graph.assertActive();
3411
4072
  return this.createSpaceScopedKVService(spaceId);
3412
4073
  },
3413
4074
  createVaultService: (spaceId) => {
4075
+ graph.assertActive();
3414
4076
  const kvService = this.createSpaceScopedKVService(spaceId);
3415
4077
  const vaultService = this.createVaultService(spaceId, kvService);
3416
4078
  if (this._serviceContext) {
@@ -3419,11 +4081,13 @@ var _TinyCloudNode = class _TinyCloudNode {
3419
4081
  return vaultService;
3420
4082
  },
3421
4083
  createSecretsService: (spaceId) => {
4084
+ graph.assertActive();
3422
4085
  return this.secretsForSpace(spaceId);
3423
4086
  },
3424
4087
  // Enable space.delegations.create() via SIWE-based delegation
3425
4088
  createDelegation: async (params) => {
3426
4089
  try {
4090
+ graph.assertActive();
3427
4091
  const portableDelegation = await this.createDelegation({
3428
4092
  delegateDID: params.delegateDID,
3429
4093
  path: params.path,
@@ -3457,18 +4121,50 @@ var _TinyCloudNode = class _TinyCloudNode {
3457
4121
  }
3458
4122
  },
3459
4123
  onSpaceRegistered: async (space) => {
4124
+ graph.assertActive();
3460
4125
  await this.account.spaces.register(space);
3461
4126
  }
3462
4127
  });
3463
- this._sharingService.updateConfig({
4128
+ this._sharingService = new import_sdk_core8.SharingService({
4129
+ hosts: [this.config.host],
3464
4130
  session: serviceSession,
4131
+ invoke: graph.invoke,
4132
+ fetch: graph.fetch,
4133
+ assertActive: () => graph.assertActive(),
4134
+ keyProvider: this._keyProvider,
4135
+ registry: this._capabilityRegistry,
3465
4136
  delegationManager: this._delegationManager,
3466
4137
  sessionExpiry: this.getSessionExpiry(),
3467
- // WASM-based delegation creation (preferred - no server roundtrip)
3468
- createDelegationWasm: (params) => this.createDelegationWrapper(params),
3469
- // Root delegation for long-lived share links (bypasses session expiry)
3470
- // In node-sdk we have direct signer access, so no popup needed
3471
- onRootDelegationNeeded: this.signer ? async (params) => this.createRootDelegationForSharing(params) : void 0
4138
+ createDelegationWasm: (params) => {
4139
+ graph.assertActive();
4140
+ return this.createDelegationWrapper(params);
4141
+ },
4142
+ computeCid: (data, codec) => {
4143
+ if (!this.wasmBindings.computeCid) throw new Error("computeCid is unavailable");
4144
+ return this.wasmBindings.computeCid(data, codec);
4145
+ },
4146
+ createKVService: (config) => {
4147
+ graph.assertActive();
4148
+ const service = new import_sdk_core8.KVService({ prefix: config.pathPrefix?.replace(/\/$/, "") });
4149
+ const context = graph.track(new import_sdk_core8.ServiceContext({
4150
+ invoke: config.invoke,
4151
+ fetch: config.fetch ?? graph.fetch,
4152
+ hosts: config.hosts,
4153
+ telemetry: this.config.telemetry
4154
+ }));
4155
+ context.setSession(config.session);
4156
+ service.initialize(context);
4157
+ return service;
4158
+ },
4159
+ onRootDelegationNeeded: this.signer ? async (params) => {
4160
+ graph.assertActive();
4161
+ const delegation = await this.createRootDelegationForSharing(
4162
+ params,
4163
+ () => graph.assertActive()
4164
+ );
4165
+ graph.assertActive();
4166
+ return delegation;
4167
+ } : void 0
3472
4168
  });
3473
4169
  this._spaceService.updateConfig({
3474
4170
  sharingService: this._sharingService
@@ -3535,7 +4231,9 @@ var _TinyCloudNode = class _TinyCloudNode {
3535
4231
  * with expiry longer than the current session.
3536
4232
  * @internal
3537
4233
  */
3538
- async createOwnerDelegation(params) {
4234
+ async createOwnerDelegation(params, assertActive) {
4235
+ const assertOwnerGraphActive = assertActive ?? this._serviceGraph.assertActive.bind(this._serviceGraph);
4236
+ assertOwnerGraphActive();
3539
4237
  if (!params.delegateDid.startsWith("did:key:") || params.actions.length === 0 || params.path.length === 0) {
3540
4238
  throw new Error("Owner delegation requires an external did:key audience and bounded capabilities");
3541
4239
  }
@@ -3560,8 +4258,10 @@ var _TinyCloudNode = class _TinyCloudNode {
3560
4258
  delegateUri: params.delegateDid
3561
4259
  });
3562
4260
  const signature = await this.signer.signMessage(prepared.siwe);
4261
+ assertOwnerGraphActive();
3563
4262
  const delegationSession = this.wasmBindings.completeSessionSetup({ ...prepared, signature });
3564
4263
  const activation = await (0, import_sdk_core8.activateSessionWithHost)(host, delegationSession.delegationHeader);
4264
+ assertOwnerGraphActive();
3565
4265
  if (!activation.success) {
3566
4266
  throw new Error(`Owner delegation import failed: ${activation.status} ${activation.error ?? ""}`.trim());
3567
4267
  }
@@ -3589,7 +4289,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3589
4289
  }
3590
4290
  };
3591
4291
  }
3592
- async createRootDelegationForSharing(params) {
4292
+ async createRootDelegationForSharing(params, assertActive) {
3593
4293
  try {
3594
4294
  return (await this.createOwnerDelegation({
3595
4295
  delegateDid: params.shareKeyDID,
@@ -3597,8 +4297,9 @@ var _TinyCloudNode = class _TinyCloudNode {
3597
4297
  path: params.path,
3598
4298
  actions: params.actions,
3599
4299
  expiresAt: params.requestedExpiry
3600
- })).delegation;
4300
+ }, assertActive)).delegation;
3601
4301
  } catch {
4302
+ assertActive?.();
3602
4303
  return void 0;
3603
4304
  }
3604
4305
  }
@@ -3670,13 +4371,13 @@ var _TinyCloudNode = class _TinyCloudNode {
3670
4371
  throw new Error("Not signed in. Call signIn() first.");
3671
4372
  }
3672
4373
  const sql = new import_sdk_core8.SQLService({});
3673
- const spaceScopedContext = new import_sdk_core8.ServiceContext({
4374
+ const spaceScopedContext = this._serviceGraph.track(new import_sdk_core8.ServiceContext({
3674
4375
  invoke: this._serviceContext.invoke,
3675
4376
  invokeAny: this._serviceContext.invokeAny,
3676
4377
  fetch: this._serviceContext.fetch,
3677
4378
  hosts: this._serviceContext.hosts,
3678
4379
  telemetry: this.config.telemetry
3679
- });
4380
+ }));
3680
4381
  spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
3681
4382
  sql.initialize(spaceScopedContext);
3682
4383
  return sql;
@@ -3700,12 +4401,12 @@ var _TinyCloudNode = class _TinyCloudNode {
3700
4401
  throw new Error("Not signed in. Call signIn() first.");
3701
4402
  }
3702
4403
  const kv = new import_sdk_core8.KVService({});
3703
- const spaceScopedContext = new import_sdk_core8.ServiceContext({
4404
+ const spaceScopedContext = this._serviceGraph.track(new import_sdk_core8.ServiceContext({
3704
4405
  invoke: this._serviceContext.invoke,
3705
4406
  invokeAny: this._serviceContext.invokeAny,
3706
4407
  fetch: this._serviceContext.fetch,
3707
4408
  hosts: this._serviceContext.hosts
3708
- });
4409
+ }));
3709
4410
  spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
3710
4411
  kv.initialize(spaceScopedContext);
3711
4412
  return kv;
@@ -3741,19 +4442,11 @@ var _TinyCloudNode = class _TinyCloudNode {
3741
4442
  }
3742
4443
  async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
3743
4444
  const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
3744
- const response = await fetch(
3745
- `${this.config.host}/encryption/networks/${encodeURIComponent(networkId)}`
4445
+ return this.fetchEncryptionNetworkAt(
4446
+ this.config.host,
4447
+ networkId,
4448
+ globalThis.fetch.bind(globalThis)
3746
4449
  );
3747
- if (response.status === 404) {
3748
- return null;
3749
- }
3750
- if (!response.ok) {
3751
- throw new Error(
3752
- `Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
3753
- );
3754
- }
3755
- const body = await response.json();
3756
- return "descriptor" in body && body.descriptor ? body.descriptor : body;
3757
4450
  }
3758
4451
  async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
3759
4452
  const targetNode = await this.fetchNodeId();
@@ -3847,6 +4540,38 @@ var _TinyCloudNode = class _TinyCloudNode {
3847
4540
  }
3848
4541
  return secrets;
3849
4542
  }
4543
+ /**
4544
+ * Read a secret from an explicit target space without requesting or
4545
+ * auto-signing authority. Active runtime delegations are selected by the
4546
+ * normal KV and encryption invocation paths.
4547
+ */
4548
+ async readSecret(input) {
4549
+ const resolved = (0, import_sdk_core8.resolveSecretPath)(input.name, { scope: input.scope });
4550
+ const targetSpace = input.space.startsWith("tinycloud:") ? input.space : this.ownedSpaceId(input.space);
4551
+ const result = await this.getBaseVault(targetSpace).readNetworkEncrypted(
4552
+ resolved.vaultKey
4553
+ );
4554
+ if (result.status !== "ok") {
4555
+ if (result.status === "permission_required") {
4556
+ const hint = (0, import_sdk_services.parsePermissionHint)(result.hint);
4557
+ if (hint === void 0) return { status: "read_failed" };
4558
+ return {
4559
+ status: "permission_required",
4560
+ hint: {
4561
+ service: hint.service,
4562
+ ...hint.space === void 0 ? {} : { space: hint.space },
4563
+ path: hint.path,
4564
+ actions: [...hint.actions]
4565
+ }
4566
+ };
4567
+ }
4568
+ return { status: result.status };
4569
+ }
4570
+ if (!isSecretPayload(result.entry.value)) {
4571
+ return { status: "invalid_payload" };
4572
+ }
4573
+ return { status: "ok", value: result.entry.value.value };
4574
+ }
3850
4575
  getBaseSecrets(spaceId) {
3851
4576
  if (!this._spaceService) {
3852
4577
  throw new Error("Not signed in. Call signIn() first.");
@@ -3854,16 +4579,29 @@ var _TinyCloudNode = class _TinyCloudNode {
3854
4579
  const resolvedSpace = spaceId.startsWith("tinycloud:") ? spaceId : this.ownedSpaceId(spaceId);
3855
4580
  let secrets = this._baseSecrets.get(resolvedSpace);
3856
4581
  if (!secrets) {
3857
- const kvService = this.createSpaceScopedKVService(resolvedSpace);
3858
- const vaultService = this.createVaultService(resolvedSpace, kvService);
3859
- if (this._serviceContext) {
3860
- vaultService.initialize(this._serviceContext);
3861
- }
3862
- secrets = new import_sdk_core8.SecretsService(() => vaultService);
4582
+ secrets = new import_sdk_core8.SecretsService(() => this.getBaseVault(resolvedSpace));
3863
4583
  this._baseSecrets.set(resolvedSpace, secrets);
3864
4584
  }
3865
4585
  return secrets;
3866
4586
  }
4587
+ getBaseVault(spaceId) {
4588
+ if (!this._spaceService) {
4589
+ throw new Error("Not signed in. Call signIn() first.");
4590
+ }
4591
+ const resolvedSpace = spaceId.startsWith("tinycloud:") ? spaceId : this.ownedSpaceId(spaceId);
4592
+ let vault = this._baseVaults.get(resolvedSpace);
4593
+ if (!vault) {
4594
+ vault = this.createVaultService(
4595
+ resolvedSpace,
4596
+ this.createSpaceScopedKVService(resolvedSpace)
4597
+ );
4598
+ if (this._serviceContext) {
4599
+ vault.initialize(this._serviceContext);
4600
+ }
4601
+ this._baseVaults.set(resolvedSpace, vault);
4602
+ }
4603
+ return vault;
4604
+ }
3867
4605
  /**
3868
4606
  * Hooks write stream subscription API.
3869
4607
  */
@@ -3968,6 +4706,63 @@ var _TinyCloudNode = class _TinyCloudNode {
3968
4706
  (grant) => grant.delegation
3969
4707
  );
3970
4708
  }
4709
+ /**
4710
+ * Return the effective capabilities of installed runtime grants.
4711
+ *
4712
+ * The result is projected from the SDK's activated, expiry-pruned grant
4713
+ * operations rather than from PortableDelegation transport metadata. Base
4714
+ * session authority is intentionally omitted; callers that need the full
4715
+ * live authority must combine this with {@link getVerifiedSessionCapabilities}.
4716
+ */
4717
+ getEffectiveRuntimePermissionEntries() {
4718
+ const session = this.currentTinyCloudSession();
4719
+ if (!session) return [];
4720
+ this.pruneExpiredRuntimePermissionGrants();
4721
+ const grouped = /* @__PURE__ */ new Map();
4722
+ for (const grant of this.runtimePermissionGrants) {
4723
+ if (grant.provenance === "primary") continue;
4724
+ for (const operation of grant.operations) {
4725
+ const service = import_sdk_core8.SERVICE_SHORT_TO_LONG[operation.service];
4726
+ if (service === void 0 || typeof operation.path !== "string" || typeof operation.action !== "string") {
4727
+ continue;
4728
+ }
4729
+ let space;
4730
+ if (service !== "tinycloud.encryption") {
4731
+ if (typeof operation.spaceId !== "string") continue;
4732
+ try {
4733
+ space = this.resolvePermissionSpace(operation.spaceId, session);
4734
+ } catch {
4735
+ continue;
4736
+ }
4737
+ }
4738
+ const caveats = operation.caveats === void 0 || operation.caveats.length === 0 ? void 0 : cloneRecapCaveats(operation.caveats);
4739
+ const identity = JSON.stringify({
4740
+ service,
4741
+ ...space === void 0 ? {} : { space },
4742
+ path: operation.path,
4743
+ caveats: (0, import_sdk_core8.canonicalizeRecapCaveats)(caveats)
4744
+ });
4745
+ const existing = grouped.get(identity);
4746
+ if (existing === void 0) {
4747
+ grouped.set(identity, {
4748
+ service,
4749
+ ...space === void 0 ? {} : { space },
4750
+ path: operation.path,
4751
+ actions: [operation.action],
4752
+ ...caveats === void 0 ? {} : { caveats }
4753
+ });
4754
+ continue;
4755
+ }
4756
+ if (!existing.actions.includes(operation.action)) {
4757
+ existing.actions.push(operation.action);
4758
+ existing.actions.sort();
4759
+ }
4760
+ }
4761
+ }
4762
+ return [...grouped.values()].sort(
4763
+ (left, right) => left.service.localeCompare(right.service) || (left.space ?? "").localeCompare(right.space ?? "") || left.path.localeCompare(right.path) || left.actions.join("\0").localeCompare(right.actions.join("\0"))
4764
+ );
4765
+ }
3971
4766
  /**
3972
4767
  * Install a portable runtime permission delegation into this SDK instance so
3973
4768
  * matching service calls and downstream `delegateTo()` calls can use it.
@@ -4036,6 +4831,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4036
4831
  "grantRuntimePermissions requires wallet mode with a signer or privateKey."
4037
4832
  );
4038
4833
  }
4834
+ this.assertDelegationCaveatsPreservable(expanded);
4039
4835
  const rawEntries = expanded.filter(
4040
4836
  (entry) => this.isEncryptionPermissionEntry(entry)
4041
4837
  );
@@ -4297,12 +5093,12 @@ var _TinyCloudNode = class _TinyCloudNode {
4297
5093
  }
4298
5094
  if (this._serviceContext) {
4299
5095
  const publicKV = new import_sdk_core8.KVService({ prefix: "" });
4300
- const publicContext = new import_sdk_core8.ServiceContext({
4301
- invoke: this.invokeWithRuntimePermissions,
4302
- fetch: this._serviceContext.fetch,
5096
+ const publicContext = this._serviceGraph.track(new import_sdk_core8.ServiceContext({
5097
+ invoke: this._serviceGraph.invoke,
5098
+ fetch: this._serviceGraph.fetch,
4303
5099
  hosts: this._serviceContext.hosts,
4304
5100
  telemetry: this.config.telemetry
4305
- });
5101
+ }));
4306
5102
  publicContext.setSession({
4307
5103
  delegationHeader: delegationSession.delegationHeader,
4308
5104
  delegationCid: delegationSession.delegationCid,
@@ -4369,13 +5165,14 @@ var _TinyCloudNode = class _TinyCloudNode {
4369
5165
  async getDelegationStatus(cid) {
4370
5166
  return this.delegationManager.status(cid);
4371
5167
  }
4372
- /** Compute the canonical CID of a compact delegation authorization. */
5168
+ /** Compute the canonical CID of a compact UCAN or DAG-CBOR delegation authorization. */
4373
5169
  computeDelegationCid(authorization) {
4374
5170
  if (!authorization || !this.wasmBindings.computeCid) {
4375
5171
  throw new Error("Delegation CID computation is unavailable");
4376
5172
  }
5173
+ const compact = authorization.replace(/^Bearer /i, "");
4377
5174
  return this.wasmBindings.computeCid(
4378
- new TextEncoder().encode(authorization),
5175
+ compact.includes(".") ? new TextEncoder().encode(compact) : decodeAuthorizationBytes(authorization),
4379
5176
  0x55n
4380
5177
  );
4381
5178
  }
@@ -4467,6 +5264,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4467
5264
  "delegateTo with forceWalletSign=true supports at most one PermissionEntry. Multi-entry requests must go through the session-key UCAN path (drop forceWalletSign) or the legacy createDelegation method."
4468
5265
  );
4469
5266
  }
5267
+ this.assertDelegationCaveatsPreservable(expandedEntries);
4470
5268
  const delegation2 = await this.createDelegationLegacyWalletPath(
4471
5269
  did,
4472
5270
  expandedEntries[0],
@@ -4475,13 +5273,14 @@ var _TinyCloudNode = class _TinyCloudNode {
4475
5273
  return { delegation: delegation2, prompted: true };
4476
5274
  }
4477
5275
  const granted = (0, import_sdk_core8.parseRecapCapabilities)(
4478
- (siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
5276
+ (siwe) => this.parseRecapWithCaveats(siwe),
4479
5277
  session.siwe
4480
5278
  );
4481
5279
  const { subset, missing } = (0, import_sdk_core8.isCapabilitySubset)(expandedEntries, granted);
4482
5280
  if (!subset) {
5281
+ const runtimeOperations = this.permissionEntriesToOperations(expandedEntries, session);
4483
5282
  const runtimeGrant = this.findGrantForOperations(
4484
- this.permissionEntriesToOperations(expandedEntries, session),
5283
+ runtimeOperations,
4485
5284
  { excludePrimary: true }
4486
5285
  );
4487
5286
  if (runtimeGrant) {
@@ -4494,7 +5293,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4494
5293
  did,
4495
5294
  expandedEntries,
4496
5295
  runtimeExpiration,
4497
- runtimeGrant
5296
+ runtimeGrant,
5297
+ runtimeOperations
4498
5298
  );
4499
5299
  return { delegation: delegation2, prompted: false };
4500
5300
  }
@@ -4564,6 +5364,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4564
5364
  "createDelegationViaWasmPath requires a non-empty entries array"
4565
5365
  );
4566
5366
  }
5367
+ this.assertDelegationCaveatsPreservable(entries);
4567
5368
  const resolvedSpaces = /* @__PURE__ */ new Set();
4568
5369
  for (const entry of entries) {
4569
5370
  if (this.isEncryptionPermissionEntry(entry)) {
@@ -4644,7 +5445,9 @@ var _TinyCloudNode = class _TinyCloudNode {
4644
5445
  host: this.config.host
4645
5446
  };
4646
5447
  }
4647
- async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant) {
5448
+ async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant, requestedOperations) {
5449
+ this.assertRuntimeGrantCaveatsPreservable(entries, requestedOperations, grant);
5450
+ this.assertDelegationCaveatsPreservable(entries);
4648
5451
  const result = this.createDelegationWrapper({
4649
5452
  session: grant.session,
4650
5453
  delegateDID: did,
@@ -4698,6 +5501,74 @@ var _TinyCloudNode = class _TinyCloudNode {
4698
5501
  expandPermissionEntries(permissions) {
4699
5502
  return (0, import_sdk_core8.expandPermissionEntries)(permissions);
4700
5503
  }
5504
+ /**
5505
+ * The current WASM child-delegation APIs accept action-only maps. Refuse any
5506
+ * constrained branch rather than signing a broader action-only child UCAN.
5507
+ */
5508
+ assertDelegationCaveatsPreservable(entries) {
5509
+ const caveated = entries.filter(
5510
+ (entry) => !(0, import_sdk_core8.recapCaveatsEqual)(entry.caveats, void 0)
5511
+ );
5512
+ if (caveated.length > 0) {
5513
+ throw new import_sdk_core8.CaveatedDelegationUnsupportedError(caveated);
5514
+ }
5515
+ }
5516
+ /**
5517
+ * Runtime grant selection intentionally allows an uncaveated operation to
5518
+ * select a caveated grant so invocation can restore the signed caveat before
5519
+ * crossing the caveat-aware WASM boundary. Child delegation cannot do that:
5520
+ * it only accepts action maps, so reject the selected constrained branch.
5521
+ */
5522
+ assertRuntimeGrantCaveatsPreservable(entries, requestedOperations, grant) {
5523
+ let operationIndex = 0;
5524
+ const caveated = [];
5525
+ for (const entry of entries) {
5526
+ for (const action of entry.actions) {
5527
+ const requested = requestedOperations[operationIndex++];
5528
+ if (!requested) continue;
5529
+ const constrained = grant.operations.find(
5530
+ (granted) => this.operationCovers(granted, requested) && !(0, import_sdk_core8.recapCaveatsEqual)(granted.caveats, void 0)
5531
+ );
5532
+ if (constrained) {
5533
+ caveated.push({
5534
+ ...entry,
5535
+ actions: [action],
5536
+ caveats: cloneRecapCaveats(constrained.caveats)
5537
+ });
5538
+ }
5539
+ }
5540
+ }
5541
+ if (caveated.length > 0) {
5542
+ throw new import_sdk_core8.CaveatedDelegationUnsupportedError(caveated);
5543
+ }
5544
+ }
5545
+ /** Reject caveated parent branches before action-only child signing. */
5546
+ assertPortableDelegationCaveatsPreservable(delegation) {
5547
+ const entries = [];
5548
+ const add = (service, space, path, actions, caveats) => {
5549
+ if ((0, import_sdk_core8.recapCaveatsEqual)(caveats, void 0)) return;
5550
+ entries.push({
5551
+ service: service.startsWith("tinycloud.") ? service : `tinycloud.${service}`,
5552
+ space,
5553
+ path,
5554
+ actions: [...actions],
5555
+ caveats: cloneRecapCaveats(caveats)
5556
+ });
5557
+ };
5558
+ add(
5559
+ delegation.actions[0]?.split("/", 1)[0] ?? "tinycloud.unknown",
5560
+ delegation.spaceId,
5561
+ delegation.path,
5562
+ delegation.actions,
5563
+ delegation.caveats
5564
+ );
5565
+ for (const resource of delegation.resources ?? []) {
5566
+ add(resource.service, resource.space, resource.path, resource.actions, resource.caveats);
5567
+ }
5568
+ if (entries.length > 0) {
5569
+ throw new import_sdk_core8.CaveatedDelegationUnsupportedError(entries);
5570
+ }
5571
+ }
4701
5572
  shortServiceName(service) {
4702
5573
  const short = import_sdk_core8.SERVICE_LONG_TO_SHORT[service];
4703
5574
  if (short === void 0) {
@@ -4752,14 +5623,15 @@ var _TinyCloudNode = class _TinyCloudNode {
4752
5623
  ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
4753
5624
  service,
4754
5625
  path: entry.path,
4755
- action
5626
+ action,
5627
+ ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
4756
5628
  }));
4757
5629
  });
4758
5630
  }
4759
5631
  sessionCoversPermissionEntries(session, entries) {
4760
5632
  try {
4761
5633
  const granted = (0, import_sdk_core8.parseRecapCapabilities)(
4762
- (siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
5634
+ (siwe) => this.parseRecapWithCaveats(siwe),
4763
5635
  session.siwe
4764
5636
  );
4765
5637
  return (0, import_sdk_core8.isCapabilitySubset)(entries, granted).subset;
@@ -4775,7 +5647,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4775
5647
  ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
4776
5648
  service,
4777
5649
  path: entry.path,
4778
- action
5650
+ action,
5651
+ ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
4779
5652
  }));
4780
5653
  });
4781
5654
  }
@@ -4851,7 +5724,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4851
5724
  service: this.shortServiceName(entry.service),
4852
5725
  space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
4853
5726
  path: entry.path,
4854
- actions: [...entry.actions]
5727
+ actions: [...entry.actions],
5728
+ ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
4855
5729
  }));
4856
5730
  }
4857
5731
  operationsFromDelegation(delegation) {
@@ -4862,7 +5736,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4862
5736
  ...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
4863
5737
  service,
4864
5738
  path: resource.path,
4865
- action
5739
+ action,
5740
+ caveats: cloneRecapCaveats(resource.caveats)
4866
5741
  }));
4867
5742
  });
4868
5743
  }
@@ -4878,7 +5753,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4878
5753
  service,
4879
5754
  space: delegation.spaceId,
4880
5755
  path: delegation.path,
4881
- actions
5756
+ actions,
5757
+ ...delegation.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(delegation.caveats) }
4882
5758
  }));
4883
5759
  }
4884
5760
  /**
@@ -4979,6 +5855,9 @@ var _TinyCloudNode = class _TinyCloudNode {
4979
5855
  if (granted.service !== requested.service || !this.actionContains(granted.action, requested.action)) {
4980
5856
  return false;
4981
5857
  }
5858
+ if (requested.caveats !== void 0 && !(0, import_sdk_core8.recapCaveatsEqual)(granted.caveats, requested.caveats)) {
5859
+ return false;
5860
+ }
4982
5861
  if (granted.resource !== void 0 || requested.resource !== void 0) {
4983
5862
  return granted.resource !== void 0 && requested.resource !== void 0 && granted.resource === requested.resource && this.pathContains(granted.path, requested.path);
4984
5863
  }
@@ -5013,6 +5892,10 @@ var _TinyCloudNode = class _TinyCloudNode {
5013
5892
  invocationServiceName(service) {
5014
5893
  return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
5015
5894
  }
5895
+ /** Prefer the v2 caveat-preserving parser while retaining old custom WASM. */
5896
+ parseRecapWithCaveats(siwe) {
5897
+ return this.wasmBindings.parseVerifiedRecapFromSiwe?.(siwe) ?? this.wasmBindings.parseRecapFromSiwe(siwe);
5898
+ }
5016
5899
  isEncryptionNetworkOperation(service, path) {
5017
5900
  return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
5018
5901
  }
@@ -5023,7 +5906,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5023
5906
  resource: entry.resource,
5024
5907
  service,
5025
5908
  path: entry.path,
5026
- action: entry.action
5909
+ action: entry.action,
5910
+ ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
5027
5911
  };
5028
5912
  }
5029
5913
  if (this.isEncryptionNetworkOperation(service, entry.path)) {
@@ -5031,7 +5915,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5031
5915
  resource: entry.path,
5032
5916
  service,
5033
5917
  path: entry.path,
5034
- action: entry.action
5918
+ action: entry.action,
5919
+ ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
5035
5920
  };
5036
5921
  }
5037
5922
  if (typeof entry.spaceId === "string") {
@@ -5039,7 +5924,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5039
5924
  spaceId: entry.spaceId,
5040
5925
  service,
5041
5926
  path: entry.path,
5042
- action: entry.action
5927
+ action: entry.action,
5928
+ ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
5043
5929
  };
5044
5930
  }
5045
5931
  return void 0;
@@ -5379,6 +6265,7 @@ var _TinyCloudNode = class _TinyCloudNode {
5379
6265
  * @returns A portable delegation for the sub-delegate
5380
6266
  */
5381
6267
  async createSubDelegation(parentDelegation, params) {
6268
+ this.assertPortableDelegationCaveatsPreservable(parentDelegation);
5382
6269
  if (!this.signer) {
5383
6270
  throw new Error("Cannot createSubDelegation() in session-only mode. Requires wallet mode.");
5384
6271
  }
@@ -5471,9 +6358,10 @@ _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS = 6e4;
5471
6358
  var TinyCloudNode = _TinyCloudNode;
5472
6359
 
5473
6360
  // src/core.ts
5474
- var import_sdk_core11 = require("@tinycloud/sdk-core");
6361
+ var import_sdk_core12 = require("@tinycloud/sdk-core");
5475
6362
 
5476
6363
  // src/delegation.ts
6364
+ var import_sdk_core9 = require("@tinycloud/sdk-core");
5477
6365
  function serializeDelegation(delegation) {
5478
6366
  return JSON.stringify({
5479
6367
  ...delegation,
@@ -5490,7 +6378,6 @@ function deserializeDelegation(data) {
5490
6378
  }
5491
6379
 
5492
6380
  // src/core.ts
5493
- var import_sdk_core12 = require("@tinycloud/sdk-core");
5494
6381
  var import_sdk_core13 = require("@tinycloud/sdk-core");
5495
6382
  var import_sdk_core14 = require("@tinycloud/sdk-core");
5496
6383
  var import_sdk_core15 = require("@tinycloud/sdk-core");
@@ -5499,6 +6386,7 @@ var import_sdk_core17 = require("@tinycloud/sdk-core");
5499
6386
  var import_sdk_core18 = require("@tinycloud/sdk-core");
5500
6387
  var import_sdk_core19 = require("@tinycloud/sdk-core");
5501
6388
  var import_sdk_core20 = require("@tinycloud/sdk-core");
6389
+ var import_sdk_core21 = require("@tinycloud/sdk-core");
5502
6390
  // Annotate the CommonJS export names for ESM import in node:
5503
6391
  0 && (module.exports = {
5504
6392
  ACCOUNT_REGISTRY_PATH,
@@ -5507,6 +6395,7 @@ var import_sdk_core20 = require("@tinycloud/sdk-core");
5507
6395
  AutoApproveSpaceCreationHandler,
5508
6396
  CapabilityKeyRegistry,
5509
6397
  CapabilityKeyRegistryErrorCodes,
6398
+ CaveatedDelegationUnsupportedError,
5510
6399
  DEFAULT_MANIFEST_SPACE,
5511
6400
  DEFAULT_MANIFEST_VERSION,
5512
6401
  DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
@@ -5542,6 +6431,7 @@ var import_sdk_core20 = require("@tinycloud/sdk-core");
5542
6431
  TinyCloudDebugLogger,
5543
6432
  TinyCloudNode,
5544
6433
  UnsupportedFeatureError,
6434
+ UnsupportedSessionRestoreError,
5545
6435
  VAULT_PERMISSION_SERVICE,
5546
6436
  VaultHeaders,
5547
6437
  VaultPublicSpaceKVActions,