@tinycloud/node-sdk 2.7.0-beta.2 → 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
  */
@@ -2119,6 +2348,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2119
2348
  getPrimarySpaceId: () => this.spaceId,
2120
2349
  getAccountSpaceId: () => this.accountSpaceId,
2121
2350
  getSpaces: () => this.spaces,
2351
+ getDelegationManager: () => this.delegationManager,
2122
2352
  getAccountDb: () => this.accountSpaceId ? this.sqlForSpace(this.accountSpaceId).db("account") : void 0,
2123
2353
  ensureAccountSpaceHosted: async () => {
2124
2354
  if (this.accountSpaceId && this.auth) {
@@ -2165,6 +2395,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2165
2395
  this._hooks = void 0;
2166
2396
  this._vault = void 0;
2167
2397
  this._encryption = void 0;
2398
+ this._baseVaults.clear();
2168
2399
  this._baseSecrets.clear();
2169
2400
  this._secrets.clear();
2170
2401
  this._spaceService = void 0;
@@ -2172,6 +2403,10 @@ var _TinyCloudNode = class _TinyCloudNode {
2172
2403
  this.runtimePermissionGrants = [];
2173
2404
  await this.tc.signIn(options);
2174
2405
  this.syncResolvedHostFromAuth();
2406
+ const oldGraph = this._serviceGraph;
2407
+ this._serviceGraph = this.createServiceGraphLifetime();
2408
+ oldGraph.retire();
2409
+ this.tc.retireServices();
2175
2410
  const signedInSession = this.currentTinyCloudSession();
2176
2411
  if (signedInSession) {
2177
2412
  this.sessionKeyId = signedInSession.sessionKey;
@@ -2456,7 +2691,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2456
2691
  }
2457
2692
  let operations = [];
2458
2693
  try {
2459
- const entries = this.wasmBindings.parseRecapFromSiwe(siwe);
2694
+ const entries = this.parseRecapWithCaveats(siwe);
2460
2695
  if (Array.isArray(entries)) {
2461
2696
  operations = entries.flatMap((entry) => {
2462
2697
  const service = this.invocationServiceName(entry.service);
@@ -2464,7 +2699,8 @@ var _TinyCloudNode = class _TinyCloudNode {
2464
2699
  ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
2465
2700
  service,
2466
2701
  path: entry.path,
2467
- action
2702
+ action,
2703
+ caveats: cloneRecapCaveats(entry.caveats)
2468
2704
  }));
2469
2705
  });
2470
2706
  }
@@ -2817,86 +3053,441 @@ var _TinyCloudNode = class _TinyCloudNode {
2817
3053
  */
2818
3054
  async restoreSession(sessionData) {
2819
3055
  await this.wasmBindings.ensureInitialized?.();
2820
- this._kv = void 0;
2821
- this._sql = void 0;
2822
- this._duckdb = void 0;
2823
- this._hooks = void 0;
2824
- this._vault = void 0;
2825
- this._encryption = void 0;
2826
- this._baseSecrets.clear();
2827
- this._secrets.clear();
2828
- this._spaceService = void 0;
2829
- this._serviceContext = void 0;
2830
- this.runtimePermissionGrants = [];
2831
- const restoredAddress = sessionData.address ? (0, import_sdk_core8.canonicalizeAddress)(sessionData.address) : void 0;
2832
- if (restoredAddress) {
2833
- 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.");
2834
3089
  }
2835
- if (sessionData.chainId) {
2836
- 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);
2837
3149
  }
2838
3150
  const resolvedHost = await this.resolveRestoredHost(
2839
3151
  sessionData.tinycloudHosts,
2840
3152
  restoredAddress,
2841
3153
  sessionData.chainId
2842
3154
  );
2843
- if (resolvedHost) {
2844
- this.config.host = resolvedHost;
2845
- }
2846
- this._serviceContext = new import_sdk_core8.ServiceContext({
2847
- invoke: this.invokeWithRuntimePermissions,
2848
- invokeAny: this.invokeAnyWithRuntimePermissions,
2849
- fetch: globalThis.fetch.bind(globalThis),
2850
- hosts: [this.config.host],
2851
- telemetry: this.config.telemetry
2852
- });
2853
- this._kv = new import_sdk_core8.KVService({});
2854
- this._kv.initialize(this._serviceContext);
2855
- this._serviceContext.registerService("kv", this._kv);
2856
- this._sql = new import_sdk_core8.SQLService({});
2857
- this._sql.initialize(this._serviceContext);
2858
- this._serviceContext.registerService("sql", this._sql);
2859
- this._duckdb = new import_sdk_core8.DuckDbService({});
2860
- this._duckdb.initialize(this._serviceContext);
2861
- this._serviceContext.registerService("duckdb", this._duckdb);
2862
- this._hooks = new import_sdk_core8.HooksService({});
2863
- this._hooks.initialize(this._serviceContext);
2864
- 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;
2865
3159
  const serviceSession = {
2866
3160
  delegationHeader: sessionData.delegationHeader,
2867
3161
  delegationCid: sessionData.delegationCid,
2868
3162
  spaceId: sessionData.spaceId,
2869
- verificationMethod: sessionData.verificationMethod,
2870
- jwk: sessionData.jwk
3163
+ verificationMethod: restoredVerificationMethod,
3164
+ jwk: stagedJwk
2871
3165
  };
2872
- this._serviceContext.setSession(serviceSession);
2873
- this._vault = this.createVaultService(sessionData.spaceId, this._kv);
2874
- this._vault.initialize(this._serviceContext);
2875
- this._serviceContext.registerService("vault", this._vault);
2876
- this.initializeV2Services(serviceSession);
2877
- if (sessionData.siwe && restoredAddress && sessionData.chainId) {
2878
- const tcSession = {
2879
- address: restoredAddress,
2880
- chainId: sessionData.chainId,
2881
- sessionKey: JSON.stringify(sessionData.jwk),
2882
- spaceId: sessionData.spaceId,
2883
- delegationCid: sessionData.delegationCid,
2884
- delegationHeader: sessionData.delegationHeader,
2885
- verificationMethod: sessionData.verificationMethod,
2886
- jwk: sessionData.jwk,
2887
- siwe: sessionData.siwe,
2888
- signature: sessionData.signature ?? ""
2889
- };
2890
- if (this.auth) {
2891
- this.auth.setRestoredTinyCloudSession(
2892
- tcSession,
2893
- this.config.host ? [this.config.host] : void 0
2894
- );
2895
- } else {
2896
- 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);
2897
3353
  }
2898
- this.registerPrimarySessionGrant(tcSession);
2899
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 };
2900
3491
  }
2901
3492
  /**
2902
3493
  * Resolve the host a restored session should target.
@@ -3058,14 +3649,19 @@ var _TinyCloudNode = class _TinyCloudNode {
3058
3649
  if (!session) {
3059
3650
  return;
3060
3651
  }
3061
- this.tc.initializeServices(this.invokeWithRuntimePermissions, [this.config.host]);
3062
- this._serviceContext = new import_sdk_core8.ServiceContext({
3063
- invoke: this.invokeWithRuntimePermissions,
3064
- invokeAny: this.invokeAnyWithRuntimePermissions,
3065
- 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,
3066
3662
  hosts: [this.config.host],
3067
3663
  telemetry: this.config.telemetry
3068
- });
3664
+ }));
3069
3665
  this._kv = new import_sdk_core8.KVService({});
3070
3666
  this._kv.initialize(this._serviceContext);
3071
3667
  this._serviceContext.registerService("kv", this._kv);
@@ -3100,12 +3696,12 @@ var _TinyCloudNode = class _TinyCloudNode {
3100
3696
  createSpaceScopedKVService(spaceId) {
3101
3697
  const kvService = new import_sdk_core8.KVService({});
3102
3698
  if (this._serviceContext) {
3103
- const spaceScopedContext = new import_sdk_core8.ServiceContext({
3699
+ const spaceScopedContext = this._serviceGraph.track(new import_sdk_core8.ServiceContext({
3104
3700
  invoke: this._serviceContext.invoke,
3105
3701
  fetch: this._serviceContext.fetch,
3106
3702
  hosts: this._serviceContext.hosts,
3107
3703
  telemetry: this.config.telemetry
3108
- });
3704
+ }));
3109
3705
  const session = this._serviceContext.session;
3110
3706
  if (session) {
3111
3707
  spaceScopedContext.setSession({ ...session, spaceId });
@@ -3138,6 +3734,18 @@ var _TinyCloudNode = class _TinyCloudNode {
3138
3734
  }
3139
3735
  return session;
3140
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
+ }
3141
3749
  createEncryptionCrypto() {
3142
3750
  const wasm = this.wasmBindings;
3143
3751
  const columnEncrypt = (key, plaintext) => {
@@ -3193,14 +3801,15 @@ var _TinyCloudNode = class _TinyCloudNode {
3193
3801
  }
3194
3802
  return info.nodeId;
3195
3803
  }
3196
- async signRawNetworkAuthorization(input) {
3804
+ async signRawNetworkAuthorization(input, binding) {
3805
+ binding?.graph.assertActive();
3197
3806
  if (!this.wasmBindings.invokeAny) {
3198
3807
  throw new Error("WASM binding does not support raw-resource invokeAny");
3199
3808
  }
3200
3809
  if (!this.wasmBindings.computeCid) {
3201
3810
  throw new Error("WASM binding does not support invocation CID computation");
3202
3811
  }
3203
- const session = this.requireServiceSession();
3812
+ const session = binding?.session ?? this.requireEncryptionSession();
3204
3813
  const headers = this.invokeAnyWithRuntimePermissions(
3205
3814
  session,
3206
3815
  [
@@ -3219,6 +3828,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3219
3828
  input.targetNode,
3220
3829
  session.jwk
3221
3830
  );
3831
+ binding?.graph.assertActive();
3222
3832
  return {
3223
3833
  authorization: audienceBound,
3224
3834
  invocationCid: this.wasmBindings.computeCid(
@@ -3227,25 +3837,55 @@ var _TinyCloudNode = class _TinyCloudNode {
3227
3837
  )
3228
3838
  };
3229
3839
  }
3230
- 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;
3231
3860
  const crypto = this.createEncryptionCrypto();
3232
3861
  const transport = {
3233
- postDecrypt: async ({ networkId, authorization, canonicalBody }) => {
3234
- const response = await fetch(
3235
- `${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`,
3236
3866
  {
3237
3867
  method: "POST",
3238
3868
  headers: {
3239
3869
  Authorization: authorization,
3240
3870
  "Content-Type": "application/json"
3241
3871
  },
3242
- body: canonicalBody
3872
+ body: canonicalBody,
3873
+ signal
3243
3874
  }
3244
3875
  );
3876
+ graph.assertActive();
3245
3877
  if (!response.ok) {
3246
- throw new Error(
3247
- `decrypt failed ${response.status}: ${await response.text()}`
3248
- );
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);
3249
3889
  }
3250
3890
  return await response.json();
3251
3891
  }
@@ -3254,12 +3894,14 @@ var _TinyCloudNode = class _TinyCloudNode {
3254
3894
  crypto,
3255
3895
  signer: {
3256
3896
  signDecryptInvocation: async (input) => {
3897
+ graph.assertActive();
3257
3898
  const signed = await this.signRawNetworkAuthorization({
3258
3899
  targetNode: input.targetNode,
3259
3900
  networkId: input.networkId,
3260
3901
  action: DECRYPT_ACTION2,
3261
3902
  facts: input.facts
3262
- });
3903
+ }, { graph, session });
3904
+ graph.assertActive();
3263
3905
  return {
3264
3906
  ...signed,
3265
3907
  canonicalBody: (0, import_sdk_core8.canonicalizeEncryptionJson)(
@@ -3270,25 +3912,35 @@ var _TinyCloudNode = class _TinyCloudNode {
3270
3912
  },
3271
3913
  transport,
3272
3914
  node: {
3273
- 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
+ }
3274
3921
  },
3275
3922
  wellKnown: {
3276
3923
  fetchWellKnown: async (principal, discoveryKey) => {
3277
- if (!this._address || !didPrincipalMatches2(principal, this.did)) {
3278
- return null;
3279
- }
3280
- if (!this.config.host) {
3924
+ graph.assertActive();
3925
+ if (!address || !didPrincipalMatches2(principal, did)) {
3281
3926
  return null;
3282
3927
  }
3283
- const publicSpaceId = (0, import_sdk_core8.makePublicSpaceId)(this._address, this._chainId);
3284
- const result = await import_sdk_core8.TinyCloud.readPublicSpace(this.config.host, publicSpaceId, discoveryKey);
3285
- 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) {
3286
3936
  return null;
3287
3937
  }
3288
- const body = result.data;
3938
+ const body = await response.json();
3939
+ graph.assertActive();
3289
3940
  return "descriptor" in body && body.descriptor ? body.descriptor : body;
3290
3941
  }
3291
- }
3942
+ },
3943
+ assertActive: () => graph.assertActive()
3292
3944
  });
3293
3945
  }
3294
3946
  getEncryptionService() {
@@ -3302,8 +3954,9 @@ var _TinyCloudNode = class _TinyCloudNode {
3302
3954
  }
3303
3955
  return this._encryption;
3304
3956
  }
3305
- createVaultService(spaceId, kv) {
3957
+ createVaultService(spaceId, kv, encryptionService = this.getEncryptionService(), nodeContext) {
3306
3958
  const wasm = this.wasmBindings;
3959
+ const serviceGraph = nodeContext?.serviceGraph ?? this._serviceGraph;
3307
3960
  const vaultCrypto = (0, import_sdk_core8.createVaultCrypto)({
3308
3961
  vault_encrypt: wasm.vault_encrypt,
3309
3962
  vault_decrypt: wasm.vault_decrypt,
@@ -3314,12 +3967,17 @@ var _TinyCloudNode = class _TinyCloudNode {
3314
3967
  vault_sha256: wasm.vault_sha256
3315
3968
  });
3316
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;
3317
3975
  return new import_sdk_core8.DataVaultService({
3318
3976
  spaceId,
3319
3977
  crypto: vaultCrypto,
3320
3978
  encryption: {
3321
- networkId: this.getEncryptionNetworkIdForSpace(spaceId),
3322
- service: this.getEncryptionService(),
3979
+ networkId: `urn:tinycloud:encryption:${ownerDid}:${DEFAULT_ENCRYPTION_NETWORK_NAME}`,
3980
+ service: encryptionService,
3323
3981
  decryptCapabilityProof: () => ({
3324
3982
  proofs: [this.requireServiceSession().delegationCid]
3325
3983
  })
@@ -3328,6 +3986,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3328
3986
  kv,
3329
3987
  ensurePublicSpace: async () => {
3330
3988
  try {
3989
+ serviceGraph.assertActive();
3331
3990
  await self.ensurePublicSpace();
3332
3991
  return { ok: true, data: void 0 };
3333
3992
  } catch (error) {
@@ -3335,14 +3994,15 @@ var _TinyCloudNode = class _TinyCloudNode {
3335
3994
  }
3336
3995
  },
3337
3996
  get publicKV() {
3997
+ serviceGraph.assertActive();
3338
3998
  return self._publicKV ?? self.tc.publicKV;
3339
3999
  },
3340
- 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),
3341
4001
  makePublicSpaceId: import_sdk_core8.TinyCloud.makePublicSpaceId,
3342
- did: this.did,
3343
- address: this._address ?? "",
3344
- chainId: this._chainId,
3345
- hosts: [this.config.host]
4002
+ did,
4003
+ address: address ?? "",
4004
+ chainId,
4005
+ hosts: [host]
3346
4006
  }
3347
4007
  });
3348
4008
  }
@@ -3351,6 +4011,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3351
4011
  * @internal
3352
4012
  */
3353
4013
  initializeV2Services(serviceSession) {
4014
+ const graph = this._serviceGraph;
3354
4015
  this._capabilityRegistry = new import_sdk_core8.CapabilityKeyRegistry();
3355
4016
  const tcSession = this.auth?.tinyCloudSession;
3356
4017
  if (tcSession && this._address) {
@@ -3393,22 +4054,25 @@ var _TinyCloudNode = class _TinyCloudNode {
3393
4054
  }
3394
4055
  this._delegationManager = new import_sdk_core8.DelegationManager({
3395
4056
  hosts: [this.config.host],
4057
+ accountSpaceId: this.accountSpaceId,
3396
4058
  session: serviceSession,
3397
- invoke: this.invokeWithRuntimePermissions,
3398
- invokeAny: this.invokeAnyWithRuntimePermissions,
3399
- fetch: globalThis.fetch.bind(globalThis)
4059
+ invoke: graph.invoke,
4060
+ invokeAny: graph.invokeAny,
4061
+ fetch: graph.fetch
3400
4062
  });
3401
4063
  this._spaceService = new import_sdk_core8.SpaceService({
3402
4064
  hosts: [this.config.host],
3403
4065
  session: serviceSession,
3404
- invoke: this.wasmBindings.invoke,
3405
- fetch: globalThis.fetch.bind(globalThis),
4066
+ invoke: graph.invoke,
4067
+ fetch: graph.fetch,
3406
4068
  capabilityRegistry: this._capabilityRegistry,
3407
4069
  userDid: this.did,
3408
4070
  createKVService: (spaceId) => {
4071
+ graph.assertActive();
3409
4072
  return this.createSpaceScopedKVService(spaceId);
3410
4073
  },
3411
4074
  createVaultService: (spaceId) => {
4075
+ graph.assertActive();
3412
4076
  const kvService = this.createSpaceScopedKVService(spaceId);
3413
4077
  const vaultService = this.createVaultService(spaceId, kvService);
3414
4078
  if (this._serviceContext) {
@@ -3417,11 +4081,13 @@ var _TinyCloudNode = class _TinyCloudNode {
3417
4081
  return vaultService;
3418
4082
  },
3419
4083
  createSecretsService: (spaceId) => {
4084
+ graph.assertActive();
3420
4085
  return this.secretsForSpace(spaceId);
3421
4086
  },
3422
4087
  // Enable space.delegations.create() via SIWE-based delegation
3423
4088
  createDelegation: async (params) => {
3424
4089
  try {
4090
+ graph.assertActive();
3425
4091
  const portableDelegation = await this.createDelegation({
3426
4092
  delegateDID: params.delegateDID,
3427
4093
  path: params.path,
@@ -3455,18 +4121,50 @@ var _TinyCloudNode = class _TinyCloudNode {
3455
4121
  }
3456
4122
  },
3457
4123
  onSpaceRegistered: async (space) => {
4124
+ graph.assertActive();
3458
4125
  await this.account.spaces.register(space);
3459
4126
  }
3460
4127
  });
3461
- this._sharingService.updateConfig({
4128
+ this._sharingService = new import_sdk_core8.SharingService({
4129
+ hosts: [this.config.host],
3462
4130
  session: serviceSession,
4131
+ invoke: graph.invoke,
4132
+ fetch: graph.fetch,
4133
+ assertActive: () => graph.assertActive(),
4134
+ keyProvider: this._keyProvider,
4135
+ registry: this._capabilityRegistry,
3463
4136
  delegationManager: this._delegationManager,
3464
4137
  sessionExpiry: this.getSessionExpiry(),
3465
- // WASM-based delegation creation (preferred - no server roundtrip)
3466
- createDelegationWasm: (params) => this.createDelegationWrapper(params),
3467
- // Root delegation for long-lived share links (bypasses session expiry)
3468
- // In node-sdk we have direct signer access, so no popup needed
3469
- 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
3470
4168
  });
3471
4169
  this._spaceService.updateConfig({
3472
4170
  sharingService: this._sharingService
@@ -3533,7 +4231,9 @@ var _TinyCloudNode = class _TinyCloudNode {
3533
4231
  * with expiry longer than the current session.
3534
4232
  * @internal
3535
4233
  */
3536
- async createOwnerDelegation(params) {
4234
+ async createOwnerDelegation(params, assertActive) {
4235
+ const assertOwnerGraphActive = assertActive ?? this._serviceGraph.assertActive.bind(this._serviceGraph);
4236
+ assertOwnerGraphActive();
3537
4237
  if (!params.delegateDid.startsWith("did:key:") || params.actions.length === 0 || params.path.length === 0) {
3538
4238
  throw new Error("Owner delegation requires an external did:key audience and bounded capabilities");
3539
4239
  }
@@ -3558,8 +4258,10 @@ var _TinyCloudNode = class _TinyCloudNode {
3558
4258
  delegateUri: params.delegateDid
3559
4259
  });
3560
4260
  const signature = await this.signer.signMessage(prepared.siwe);
4261
+ assertOwnerGraphActive();
3561
4262
  const delegationSession = this.wasmBindings.completeSessionSetup({ ...prepared, signature });
3562
4263
  const activation = await (0, import_sdk_core8.activateSessionWithHost)(host, delegationSession.delegationHeader);
4264
+ assertOwnerGraphActive();
3563
4265
  if (!activation.success) {
3564
4266
  throw new Error(`Owner delegation import failed: ${activation.status} ${activation.error ?? ""}`.trim());
3565
4267
  }
@@ -3587,7 +4289,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3587
4289
  }
3588
4290
  };
3589
4291
  }
3590
- async createRootDelegationForSharing(params) {
4292
+ async createRootDelegationForSharing(params, assertActive) {
3591
4293
  try {
3592
4294
  return (await this.createOwnerDelegation({
3593
4295
  delegateDid: params.shareKeyDID,
@@ -3595,8 +4297,9 @@ var _TinyCloudNode = class _TinyCloudNode {
3595
4297
  path: params.path,
3596
4298
  actions: params.actions,
3597
4299
  expiresAt: params.requestedExpiry
3598
- })).delegation;
4300
+ }, assertActive)).delegation;
3599
4301
  } catch {
4302
+ assertActive?.();
3600
4303
  return void 0;
3601
4304
  }
3602
4305
  }
@@ -3668,13 +4371,13 @@ var _TinyCloudNode = class _TinyCloudNode {
3668
4371
  throw new Error("Not signed in. Call signIn() first.");
3669
4372
  }
3670
4373
  const sql = new import_sdk_core8.SQLService({});
3671
- const spaceScopedContext = new import_sdk_core8.ServiceContext({
4374
+ const spaceScopedContext = this._serviceGraph.track(new import_sdk_core8.ServiceContext({
3672
4375
  invoke: this._serviceContext.invoke,
3673
4376
  invokeAny: this._serviceContext.invokeAny,
3674
4377
  fetch: this._serviceContext.fetch,
3675
4378
  hosts: this._serviceContext.hosts,
3676
4379
  telemetry: this.config.telemetry
3677
- });
4380
+ }));
3678
4381
  spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
3679
4382
  sql.initialize(spaceScopedContext);
3680
4383
  return sql;
@@ -3698,12 +4401,12 @@ var _TinyCloudNode = class _TinyCloudNode {
3698
4401
  throw new Error("Not signed in. Call signIn() first.");
3699
4402
  }
3700
4403
  const kv = new import_sdk_core8.KVService({});
3701
- const spaceScopedContext = new import_sdk_core8.ServiceContext({
4404
+ const spaceScopedContext = this._serviceGraph.track(new import_sdk_core8.ServiceContext({
3702
4405
  invoke: this._serviceContext.invoke,
3703
4406
  invokeAny: this._serviceContext.invokeAny,
3704
4407
  fetch: this._serviceContext.fetch,
3705
4408
  hosts: this._serviceContext.hosts
3706
- });
4409
+ }));
3707
4410
  spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
3708
4411
  kv.initialize(spaceScopedContext);
3709
4412
  return kv;
@@ -3739,19 +4442,11 @@ var _TinyCloudNode = class _TinyCloudNode {
3739
4442
  }
3740
4443
  async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
3741
4444
  const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
3742
- const response = await fetch(
3743
- `${this.config.host}/encryption/networks/${encodeURIComponent(networkId)}`
4445
+ return this.fetchEncryptionNetworkAt(
4446
+ this.config.host,
4447
+ networkId,
4448
+ globalThis.fetch.bind(globalThis)
3744
4449
  );
3745
- if (response.status === 404) {
3746
- return null;
3747
- }
3748
- if (!response.ok) {
3749
- throw new Error(
3750
- `Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
3751
- );
3752
- }
3753
- const body = await response.json();
3754
- return "descriptor" in body && body.descriptor ? body.descriptor : body;
3755
4450
  }
3756
4451
  async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
3757
4452
  const targetNode = await this.fetchNodeId();
@@ -3845,6 +4540,38 @@ var _TinyCloudNode = class _TinyCloudNode {
3845
4540
  }
3846
4541
  return secrets;
3847
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
+ }
3848
4575
  getBaseSecrets(spaceId) {
3849
4576
  if (!this._spaceService) {
3850
4577
  throw new Error("Not signed in. Call signIn() first.");
@@ -3852,16 +4579,29 @@ var _TinyCloudNode = class _TinyCloudNode {
3852
4579
  const resolvedSpace = spaceId.startsWith("tinycloud:") ? spaceId : this.ownedSpaceId(spaceId);
3853
4580
  let secrets = this._baseSecrets.get(resolvedSpace);
3854
4581
  if (!secrets) {
3855
- const kvService = this.createSpaceScopedKVService(resolvedSpace);
3856
- const vaultService = this.createVaultService(resolvedSpace, kvService);
3857
- if (this._serviceContext) {
3858
- vaultService.initialize(this._serviceContext);
3859
- }
3860
- secrets = new import_sdk_core8.SecretsService(() => vaultService);
4582
+ secrets = new import_sdk_core8.SecretsService(() => this.getBaseVault(resolvedSpace));
3861
4583
  this._baseSecrets.set(resolvedSpace, secrets);
3862
4584
  }
3863
4585
  return secrets;
3864
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
+ }
3865
4605
  /**
3866
4606
  * Hooks write stream subscription API.
3867
4607
  */
@@ -3966,6 +4706,63 @@ var _TinyCloudNode = class _TinyCloudNode {
3966
4706
  (grant) => grant.delegation
3967
4707
  );
3968
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
+ }
3969
4766
  /**
3970
4767
  * Install a portable runtime permission delegation into this SDK instance so
3971
4768
  * matching service calls and downstream `delegateTo()` calls can use it.
@@ -4034,6 +4831,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4034
4831
  "grantRuntimePermissions requires wallet mode with a signer or privateKey."
4035
4832
  );
4036
4833
  }
4834
+ this.assertDelegationCaveatsPreservable(expanded);
4037
4835
  const rawEntries = expanded.filter(
4038
4836
  (entry) => this.isEncryptionPermissionEntry(entry)
4039
4837
  );
@@ -4295,12 +5093,12 @@ var _TinyCloudNode = class _TinyCloudNode {
4295
5093
  }
4296
5094
  if (this._serviceContext) {
4297
5095
  const publicKV = new import_sdk_core8.KVService({ prefix: "" });
4298
- const publicContext = new import_sdk_core8.ServiceContext({
4299
- invoke: this.invokeWithRuntimePermissions,
4300
- 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,
4301
5099
  hosts: this._serviceContext.hosts,
4302
5100
  telemetry: this.config.telemetry
4303
- });
5101
+ }));
4304
5102
  publicContext.setSession({
4305
5103
  delegationHeader: delegationSession.delegationHeader,
4306
5104
  delegationCid: delegationSession.delegationCid,
@@ -4367,13 +5165,14 @@ var _TinyCloudNode = class _TinyCloudNode {
4367
5165
  async getDelegationStatus(cid) {
4368
5166
  return this.delegationManager.status(cid);
4369
5167
  }
4370
- /** Compute the canonical CID of a compact delegation authorization. */
5168
+ /** Compute the canonical CID of a compact UCAN or DAG-CBOR delegation authorization. */
4371
5169
  computeDelegationCid(authorization) {
4372
5170
  if (!authorization || !this.wasmBindings.computeCid) {
4373
5171
  throw new Error("Delegation CID computation is unavailable");
4374
5172
  }
5173
+ const compact = authorization.replace(/^Bearer /i, "");
4375
5174
  return this.wasmBindings.computeCid(
4376
- new TextEncoder().encode(authorization),
5175
+ compact.includes(".") ? new TextEncoder().encode(compact) : decodeAuthorizationBytes(authorization),
4377
5176
  0x55n
4378
5177
  );
4379
5178
  }
@@ -4465,6 +5264,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4465
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."
4466
5265
  );
4467
5266
  }
5267
+ this.assertDelegationCaveatsPreservable(expandedEntries);
4468
5268
  const delegation2 = await this.createDelegationLegacyWalletPath(
4469
5269
  did,
4470
5270
  expandedEntries[0],
@@ -4473,13 +5273,14 @@ var _TinyCloudNode = class _TinyCloudNode {
4473
5273
  return { delegation: delegation2, prompted: true };
4474
5274
  }
4475
5275
  const granted = (0, import_sdk_core8.parseRecapCapabilities)(
4476
- (siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
5276
+ (siwe) => this.parseRecapWithCaveats(siwe),
4477
5277
  session.siwe
4478
5278
  );
4479
5279
  const { subset, missing } = (0, import_sdk_core8.isCapabilitySubset)(expandedEntries, granted);
4480
5280
  if (!subset) {
5281
+ const runtimeOperations = this.permissionEntriesToOperations(expandedEntries, session);
4481
5282
  const runtimeGrant = this.findGrantForOperations(
4482
- this.permissionEntriesToOperations(expandedEntries, session),
5283
+ runtimeOperations,
4483
5284
  { excludePrimary: true }
4484
5285
  );
4485
5286
  if (runtimeGrant) {
@@ -4492,7 +5293,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4492
5293
  did,
4493
5294
  expandedEntries,
4494
5295
  runtimeExpiration,
4495
- runtimeGrant
5296
+ runtimeGrant,
5297
+ runtimeOperations
4496
5298
  );
4497
5299
  return { delegation: delegation2, prompted: false };
4498
5300
  }
@@ -4562,6 +5364,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4562
5364
  "createDelegationViaWasmPath requires a non-empty entries array"
4563
5365
  );
4564
5366
  }
5367
+ this.assertDelegationCaveatsPreservable(entries);
4565
5368
  const resolvedSpaces = /* @__PURE__ */ new Set();
4566
5369
  for (const entry of entries) {
4567
5370
  if (this.isEncryptionPermissionEntry(entry)) {
@@ -4642,7 +5445,9 @@ var _TinyCloudNode = class _TinyCloudNode {
4642
5445
  host: this.config.host
4643
5446
  };
4644
5447
  }
4645
- async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant) {
5448
+ async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant, requestedOperations) {
5449
+ this.assertRuntimeGrantCaveatsPreservable(entries, requestedOperations, grant);
5450
+ this.assertDelegationCaveatsPreservable(entries);
4646
5451
  const result = this.createDelegationWrapper({
4647
5452
  session: grant.session,
4648
5453
  delegateDID: did,
@@ -4696,6 +5501,74 @@ var _TinyCloudNode = class _TinyCloudNode {
4696
5501
  expandPermissionEntries(permissions) {
4697
5502
  return (0, import_sdk_core8.expandPermissionEntries)(permissions);
4698
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
+ }
4699
5572
  shortServiceName(service) {
4700
5573
  const short = import_sdk_core8.SERVICE_LONG_TO_SHORT[service];
4701
5574
  if (short === void 0) {
@@ -4750,14 +5623,15 @@ var _TinyCloudNode = class _TinyCloudNode {
4750
5623
  ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
4751
5624
  service,
4752
5625
  path: entry.path,
4753
- action
5626
+ action,
5627
+ ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
4754
5628
  }));
4755
5629
  });
4756
5630
  }
4757
5631
  sessionCoversPermissionEntries(session, entries) {
4758
5632
  try {
4759
5633
  const granted = (0, import_sdk_core8.parseRecapCapabilities)(
4760
- (siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
5634
+ (siwe) => this.parseRecapWithCaveats(siwe),
4761
5635
  session.siwe
4762
5636
  );
4763
5637
  return (0, import_sdk_core8.isCapabilitySubset)(entries, granted).subset;
@@ -4773,7 +5647,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4773
5647
  ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
4774
5648
  service,
4775
5649
  path: entry.path,
4776
- action
5650
+ action,
5651
+ ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
4777
5652
  }));
4778
5653
  });
4779
5654
  }
@@ -4849,7 +5724,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4849
5724
  service: this.shortServiceName(entry.service),
4850
5725
  space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
4851
5726
  path: entry.path,
4852
- actions: [...entry.actions]
5727
+ actions: [...entry.actions],
5728
+ ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
4853
5729
  }));
4854
5730
  }
4855
5731
  operationsFromDelegation(delegation) {
@@ -4860,7 +5736,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4860
5736
  ...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
4861
5737
  service,
4862
5738
  path: resource.path,
4863
- action
5739
+ action,
5740
+ caveats: cloneRecapCaveats(resource.caveats)
4864
5741
  }));
4865
5742
  });
4866
5743
  }
@@ -4876,7 +5753,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4876
5753
  service,
4877
5754
  space: delegation.spaceId,
4878
5755
  path: delegation.path,
4879
- actions
5756
+ actions,
5757
+ ...delegation.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(delegation.caveats) }
4880
5758
  }));
4881
5759
  }
4882
5760
  /**
@@ -4977,6 +5855,9 @@ var _TinyCloudNode = class _TinyCloudNode {
4977
5855
  if (granted.service !== requested.service || !this.actionContains(granted.action, requested.action)) {
4978
5856
  return false;
4979
5857
  }
5858
+ if (requested.caveats !== void 0 && !(0, import_sdk_core8.recapCaveatsEqual)(granted.caveats, requested.caveats)) {
5859
+ return false;
5860
+ }
4980
5861
  if (granted.resource !== void 0 || requested.resource !== void 0) {
4981
5862
  return granted.resource !== void 0 && requested.resource !== void 0 && granted.resource === requested.resource && this.pathContains(granted.path, requested.path);
4982
5863
  }
@@ -5011,6 +5892,10 @@ var _TinyCloudNode = class _TinyCloudNode {
5011
5892
  invocationServiceName(service) {
5012
5893
  return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
5013
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
+ }
5014
5899
  isEncryptionNetworkOperation(service, path) {
5015
5900
  return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
5016
5901
  }
@@ -5021,7 +5906,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5021
5906
  resource: entry.resource,
5022
5907
  service,
5023
5908
  path: entry.path,
5024
- action: entry.action
5909
+ action: entry.action,
5910
+ ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
5025
5911
  };
5026
5912
  }
5027
5913
  if (this.isEncryptionNetworkOperation(service, entry.path)) {
@@ -5029,7 +5915,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5029
5915
  resource: entry.path,
5030
5916
  service,
5031
5917
  path: entry.path,
5032
- action: entry.action
5918
+ action: entry.action,
5919
+ ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
5033
5920
  };
5034
5921
  }
5035
5922
  if (typeof entry.spaceId === "string") {
@@ -5037,7 +5924,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5037
5924
  spaceId: entry.spaceId,
5038
5925
  service,
5039
5926
  path: entry.path,
5040
- action: entry.action
5927
+ action: entry.action,
5928
+ ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
5041
5929
  };
5042
5930
  }
5043
5931
  return void 0;
@@ -5377,6 +6265,7 @@ var _TinyCloudNode = class _TinyCloudNode {
5377
6265
  * @returns A portable delegation for the sub-delegate
5378
6266
  */
5379
6267
  async createSubDelegation(parentDelegation, params) {
6268
+ this.assertPortableDelegationCaveatsPreservable(parentDelegation);
5380
6269
  if (!this.signer) {
5381
6270
  throw new Error("Cannot createSubDelegation() in session-only mode. Requires wallet mode.");
5382
6271
  }
@@ -5469,9 +6358,10 @@ _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS = 6e4;
5469
6358
  var TinyCloudNode = _TinyCloudNode;
5470
6359
 
5471
6360
  // src/core.ts
5472
- var import_sdk_core11 = require("@tinycloud/sdk-core");
6361
+ var import_sdk_core12 = require("@tinycloud/sdk-core");
5473
6362
 
5474
6363
  // src/delegation.ts
6364
+ var import_sdk_core9 = require("@tinycloud/sdk-core");
5475
6365
  function serializeDelegation(delegation) {
5476
6366
  return JSON.stringify({
5477
6367
  ...delegation,
@@ -5488,7 +6378,6 @@ function deserializeDelegation(data) {
5488
6378
  }
5489
6379
 
5490
6380
  // src/core.ts
5491
- var import_sdk_core12 = require("@tinycloud/sdk-core");
5492
6381
  var import_sdk_core13 = require("@tinycloud/sdk-core");
5493
6382
  var import_sdk_core14 = require("@tinycloud/sdk-core");
5494
6383
  var import_sdk_core15 = require("@tinycloud/sdk-core");
@@ -5497,6 +6386,7 @@ var import_sdk_core17 = require("@tinycloud/sdk-core");
5497
6386
  var import_sdk_core18 = require("@tinycloud/sdk-core");
5498
6387
  var import_sdk_core19 = require("@tinycloud/sdk-core");
5499
6388
  var import_sdk_core20 = require("@tinycloud/sdk-core");
6389
+ var import_sdk_core21 = require("@tinycloud/sdk-core");
5500
6390
  // Annotate the CommonJS export names for ESM import in node:
5501
6391
  0 && (module.exports = {
5502
6392
  ACCOUNT_REGISTRY_PATH,
@@ -5505,6 +6395,7 @@ var import_sdk_core20 = require("@tinycloud/sdk-core");
5505
6395
  AutoApproveSpaceCreationHandler,
5506
6396
  CapabilityKeyRegistry,
5507
6397
  CapabilityKeyRegistryErrorCodes,
6398
+ CaveatedDelegationUnsupportedError,
5508
6399
  DEFAULT_MANIFEST_SPACE,
5509
6400
  DEFAULT_MANIFEST_VERSION,
5510
6401
  DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
@@ -5540,6 +6431,7 @@ var import_sdk_core20 = require("@tinycloud/sdk-core");
5540
6431
  TinyCloudDebugLogger,
5541
6432
  TinyCloudNode,
5542
6433
  UnsupportedFeatureError,
6434
+ UnsupportedSessionRestoreError,
5543
6435
  VAULT_PERMISSION_SERVICE,
5544
6436
  VaultHeaders,
5545
6437
  VaultPublicSpaceKVActions,