@tinycloud/node-sdk 3.1.0-beta.10 → 3.1.0-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -19735,15 +19735,21 @@ var _TinyCloudNode = class _TinyCloudNode {
19735
19735
  if (!this.wasmBindings.invokeAny) {
19736
19736
  throw new Error("WASM binding does not support invokeAny");
19737
19737
  }
19738
- const operations = entries.flatMap((entry) => {
19738
+ const routedEntries = entries;
19739
+ const operations = routedEntries.flatMap((entry) => {
19739
19740
  const operation = this.operationFromInvokeAnyEntry(entry);
19740
19741
  return operation ? [operation] : [];
19741
19742
  });
19742
- const grant = this.findGrantForOperations(operations);
19743
- const invocationSession = !grant || grant.provenance === "primary" ? session : grant.session;
19744
- const caveatPreservingEntries = grant ? entries.map((entry) => {
19743
+ const authorityCid = routedEntries.length === 1 ? routedEntries[0]?.revokeAuthorityCid : void 0;
19744
+ const explicitGrant = authorityCid === void 0 ? void 0 : this.runtimePermissionGrants.find((grant2) => grant2.delegation.cid === authorityCid);
19745
+ if (authorityCid !== void 0 && !explicitGrant) {
19746
+ throw new Error(`Explicit revocation authority ${authorityCid} is not installed.`);
19747
+ }
19748
+ const grant = explicitGrant ?? this.findGrantForOperations(operations);
19749
+ const invocationSession = explicitGrant ? explicitGrant.session : !grant || grant.provenance === "primary" ? session : grant.session;
19750
+ const caveatPreservingEntries = routedEntries.map(({ revokeAuthorityCid: _cid, ...entry }) => {
19745
19751
  const requested = this.operationFromInvokeAnyEntry(entry);
19746
- const granted = requested && grant.operations.find(
19752
+ const granted = grant && requested && grant.operations.find(
19747
19753
  (candidate) => this.operationCovers(candidate, requested)
19748
19754
  );
19749
19755
  if (!granted?.caveats?.length) {
@@ -19753,7 +19759,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19753
19759
  throw new Error("Invocation caveats do not match signed ReCap authority.");
19754
19760
  }
19755
19761
  return { ...entry, caveats: cloneRecapCaveats(granted.caveats) };
19756
- }) : entries;
19762
+ });
19757
19763
  return this.wasmBindings.invokeAny(invocationSession, caveatPreservingEntries, facts);
19758
19764
  };
19759
19765
  this.explicitHost = config.host;
@@ -20218,12 +20224,13 @@ var _TinyCloudNode = class _TinyCloudNode {
20218
20224
  if (!auth) {
20219
20225
  throw new Error("Account bootstrap requires an active wallet session");
20220
20226
  }
20227
+ const accountSpaceId = this.ownedSpaceId(import_sdk_core7.ACCOUNT_REGISTRY_SPACE);
20221
20228
  const skipped = auth.lastActivationSkippedSpaceIds;
20229
+ if (skipped.includes(accountSpaceId)) return { action: "run", mode: "fresh" };
20222
20230
  if (skipped.some((spaceId) => enshrinedSpaceIds.has(spaceId))) {
20223
- return { action: "run", mode: "fresh" };
20231
+ return { action: "run", mode: "repair" };
20224
20232
  }
20225
20233
  try {
20226
- const accountSpaceId = this.ownedSpaceId(import_sdk_core7.ACCOUNT_REGISTRY_SPACE);
20227
20234
  const markerPermission = {
20228
20235
  service: "tinycloud.kv",
20229
20236
  space: accountSpaceId,
@@ -20235,7 +20242,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20235
20242
  }
20236
20243
  const marker = await this.readBootstrapCompletionMarker();
20237
20244
  if (marker.ok) {
20238
- if (this.isAcceptedBootstrapCompletionMarker(marker.data.data)) {
20245
+ if (this.isAcceptedBootstrapCompletionMarker(marker.data.data, steps.map((step) => step.id))) {
20239
20246
  return { action: "skip" };
20240
20247
  }
20241
20248
  return { action: "run", mode: "repair" };
@@ -20285,12 +20292,16 @@ var _TinyCloudNode = class _TinyCloudNode {
20285
20292
  );
20286
20293
  }
20287
20294
  }
20288
- isAcceptedBootstrapCompletionMarker(value) {
20295
+ /**
20296
+ * A marker written before a canonical space was added triggers one bounded repair.
20297
+ */
20298
+ isAcceptedBootstrapCompletionMarker(value, canonicalStepIds) {
20289
20299
  if (typeof value !== "object" || value === null) return false;
20290
20300
  const entries = Object.entries(value);
20291
20301
  const markerValue = (name) => entries.find(([key2]) => key2 === name)?.[1];
20292
20302
  const version23 = markerValue("v");
20293
- return typeof version23 === "number" && Number.isInteger(version23) && ACCEPTED_MARKER_VERSIONS.some((acceptedVersion) => acceptedVersion === version23);
20303
+ const stepIds = markerValue("stepIds");
20304
+ return typeof version23 === "number" && Number.isInteger(version23) && ACCEPTED_MARKER_VERSIONS.some((acceptedVersion) => acceptedVersion === version23) && Array.isArray(stepIds) && canonicalStepIds.every((stepId) => stepIds.includes(stepId));
20294
20305
  }
20295
20306
  markerReadIsUnhostedSpace(meta) {
20296
20307
  return meta?.status === 404;
@@ -20377,30 +20388,39 @@ var _TinyCloudNode = class _TinyCloudNode {
20377
20388
  }
20378
20389
  }
20379
20390
  if (step.kind === "seed-spaces") {
20380
- const registered = await this.account.spaces.registerBatch(
20381
- step.spaces.map((space) => ({
20382
- spaceId: space.spaceId,
20383
- name: space.name,
20384
- ownerDid: this.did,
20385
- type: "owned",
20386
- permissions: ["*"],
20387
- status: "active"
20388
- }))
20389
- );
20390
- if (!registered.ok) {
20391
- throw Object.assign(
20392
- new Error(`Failed to seed account spaces: ${registered.error.message}`),
20393
- { cause: registered.error }
20394
- );
20395
- }
20396
- const batchError = registered.data.recoveredFromBatchError;
20397
- if (batchError) {
20398
- warnings.push({
20399
- stepId: step.id,
20400
- kind: "batch-write-reconciled",
20401
- code: batchError.code,
20402
- message: batchError.message
20403
- });
20391
+ const inputs = step.spaces.map((space) => ({
20392
+ spaceId: space.spaceId,
20393
+ name: space.name,
20394
+ ownerDid: this.did,
20395
+ type: "owned",
20396
+ permissions: ["*"],
20397
+ status: "active"
20398
+ }));
20399
+ if (mode === "fresh") {
20400
+ const registered = await this.account.spaces.registerBatch(inputs);
20401
+ if (!registered.ok) {
20402
+ throw Object.assign(
20403
+ new Error(`Failed to seed account spaces: ${registered.error.message}`),
20404
+ { cause: registered.error }
20405
+ );
20406
+ }
20407
+ const batchError = registered.data.recoveredFromBatchError;
20408
+ if (batchError) {
20409
+ warnings.push({
20410
+ stepId: step.id,
20411
+ kind: "batch-write-reconciled",
20412
+ code: batchError.code,
20413
+ message: batchError.message
20414
+ });
20415
+ }
20416
+ } else {
20417
+ const ensured = await this.account.spaces.registerMissing(inputs);
20418
+ if (!ensured.ok) {
20419
+ throw Object.assign(
20420
+ new Error(`Failed to seed account spaces: ${ensured.error.message}`),
20421
+ { cause: ensured.error }
20422
+ );
20423
+ }
20404
20424
  }
20405
20425
  }
20406
20426
  if (step.kind === "seed-applications") {
@@ -21085,6 +21105,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21085
21105
  stagedSessionExpiry,
21086
21106
  stagedRecap
21087
21107
  );
21108
+ const stagedAccountSpaceId = stagedAddress ? this.wasmBindings.makeSpaceId(stagedAddress, stagedChainId, import_sdk_core7.ACCOUNT_REGISTRY_SPACE) : void 0;
21088
21109
  const stagedGraph = this.stageRestoredServiceGraph({
21089
21110
  host: stagedHost,
21090
21111
  manager: stagedManager,
@@ -21093,6 +21114,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21093
21114
  nodeDid: stagedNodeDid,
21094
21115
  address: stagedAddress,
21095
21116
  chainId: stagedChainId,
21117
+ accountSpaceId: stagedAccountSpaceId,
21096
21118
  tinyCloudSession: stagedTcSession,
21097
21119
  sessionExpiry: stagedSessionExpiry,
21098
21120
  recap: stagedRecap
@@ -21258,6 +21280,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21258
21280
  }
21259
21281
  const delegationManager = new import_sdk_core7.DelegationManager({
21260
21282
  hosts: [input.host],
21283
+ accountSpaceId: input.accountSpaceId,
21261
21284
  session: input.serviceSession,
21262
21285
  invoke: graph.invoke,
21263
21286
  invokeAny: graph.invokeAny,
@@ -22962,6 +22985,14 @@ var _TinyCloudNode = class _TinyCloudNode {
22962
22985
  delegation.delegationHeader
22963
22986
  );
22964
22987
  if (!activateResult.success) {
22988
+ if (this.operationsFromDelegation(delegation).some(
22989
+ (operation) => operation.resource?.startsWith("urn:cid:") === true && operation.service === "delegation" && operation.action === "tinycloud.delegation/revoke"
22990
+ ) && activateResult.status === 401 && /Cannot find parent delegation/i.test(activateResult.error ?? "")) {
22991
+ throw Object.assign(
22992
+ new Error("The host cannot activate a root raw-CID revoke authority without a parent delegation."),
22993
+ { code: "RAW_RECAP_RESOURCE_UNSUPPORTED", cause: activateResult }
22994
+ );
22995
+ }
22965
22996
  throw Object.assign(
22966
22997
  new Error(`Failed to activate runtime permission delegation: ${describeHostFailure2(activateResult)}`),
22967
22998
  { cause: activateResult }
@@ -23009,10 +23040,10 @@ var _TinyCloudNode = class _TinyCloudNode {
23009
23040
  }
23010
23041
  this.assertDelegationCaveatsPreservable(expanded);
23011
23042
  const rawEntries = expanded.filter(
23012
- (entry) => this.isEncryptionPermissionEntry(entry)
23043
+ (entry) => this.isEncryptionPermissionEntry(entry) || this.isRawRevocationPermissionEntry(entry)
23013
23044
  );
23014
23045
  const spaceEntries = expanded.filter(
23015
- (entry) => !this.isEncryptionPermissionEntry(entry)
23046
+ (entry) => !this.isEncryptionPermissionEntry(entry) && !this.isRawRevocationPermissionEntry(entry)
23016
23047
  );
23017
23048
  const bySpace = /* @__PURE__ */ new Map();
23018
23049
  for (const entry of spaceEntries) {
@@ -23039,17 +23070,29 @@ var _TinyCloudNode = class _TinyCloudNode {
23039
23070
  }
23040
23071
  const delegatedEntries = [...entries, ...rawForDelegation];
23041
23072
  const abilities = this.permissionsToAbilities(entries);
23042
- const prepared = this.wasmBindings.prepareSession({
23043
- abilities,
23044
- ...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
23045
- address: this.wasmBindings.ensureEip55(session.address),
23046
- chainId: session.chainId,
23047
- domain: this.siweDomain,
23048
- issuedAt: now.toISOString(),
23049
- expirationTime: expiresAt.toISOString(),
23050
- spaceId,
23051
- jwk: session.jwk
23052
- });
23073
+ const prepared = (() => {
23074
+ try {
23075
+ return this.wasmBindings.prepareSession({
23076
+ abilities,
23077
+ ...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
23078
+ address: this.wasmBindings.ensureEip55(session.address),
23079
+ chainId: session.chainId,
23080
+ domain: this.siweDomain,
23081
+ issuedAt: now.toISOString(),
23082
+ expirationTime: expiresAt.toISOString(),
23083
+ spaceId,
23084
+ jwk: session.jwk
23085
+ });
23086
+ } catch (error) {
23087
+ if (rawForDelegation.some((entry) => this.isRawRevocationPermissionEntry(entry)) && /invalid ReCap resource URI/i.test(error instanceof Error ? error.message : String(error))) {
23088
+ throw Object.assign(
23089
+ new Error("This WASM build cannot encode raw urn:cid ReCap resources."),
23090
+ { code: "RAW_RECAP_RESOURCE_UNSUPPORTED", cause: error }
23091
+ );
23092
+ }
23093
+ throw error;
23094
+ }
23095
+ })();
23053
23096
  const signature2 = await this.signer.signMessage(prepared.siwe);
23054
23097
  const delegatedSession = this.wasmBindings.completeSessionSetup({
23055
23098
  ...prepared,
@@ -23060,6 +23103,12 @@ var _TinyCloudNode = class _TinyCloudNode {
23060
23103
  delegatedSession.delegationHeader
23061
23104
  );
23062
23105
  if (!activateResult.success) {
23106
+ if (rawForDelegation.some((entry) => this.isRawRevocationPermissionEntry(entry)) && activateResult.status === 401 && /Cannot find parent delegation/i.test(activateResult.error ?? "")) {
23107
+ throw Object.assign(
23108
+ new Error("The host cannot activate a root raw-CID revoke authority without a parent delegation."),
23109
+ { code: "RAW_RECAP_RESOURCE_UNSUPPORTED", cause: activateResult }
23110
+ );
23111
+ }
23063
23112
  throw Object.assign(
23064
23113
  new Error(`Failed to activate runtime permission delegation: ${describeHostFailure2(activateResult)}`),
23065
23114
  { cause: activateResult }
@@ -23761,6 +23810,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23761
23810
  permissionsToAbilities(entries) {
23762
23811
  const abilities = {};
23763
23812
  for (const entry of entries) {
23813
+ if (this.isRawRevocationPermissionEntry(entry)) continue;
23764
23814
  const service = this.shortServiceName(entry.service);
23765
23815
  abilities[service] ?? (abilities[service] = {});
23766
23816
  const existing = abilities[service][entry.path] ?? [];
@@ -23778,9 +23828,16 @@ var _TinyCloudNode = class _TinyCloudNode {
23778
23828
  isEncryptionPermissionEntry(entry) {
23779
23829
  return entry.service === import_sdk_core7.ENCRYPTION_PERMISSION_SERVICE && this.isEncryptionNetworkOperation("encryption", entry.path, entry.space);
23780
23830
  }
23831
+ isRawRevocationPermissionEntry(entry) {
23832
+ return entry.service === "tinycloud.delegation" && entry.path === "" && entry.space?.startsWith("urn:cid:") === true && entry.actions.includes("tinycloud.delegation/revoke");
23833
+ }
23781
23834
  permissionsToRawAbilities(entries) {
23782
23835
  const rawAbilities = {};
23783
23836
  for (const entry of entries) {
23837
+ if (this.isRawRevocationPermissionEntry(entry)) {
23838
+ rawAbilities[entry.space] = [...new Set(entry.actions)];
23839
+ continue;
23840
+ }
23784
23841
  if (!this.isEncryptionPermissionEntry(entry)) {
23785
23842
  continue;
23786
23843
  }
@@ -23800,7 +23857,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23800
23857
  return entries.flatMap((entry) => {
23801
23858
  const service = this.shortServiceName(entry.service);
23802
23859
  return entry.actions.map((action) => ({
23803
- ...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
23860
+ ...this.isRawRevocationPermissionEntry(entry) ? { resource: entry.space } : this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
23804
23861
  service,
23805
23862
  path: entry.path,
23806
23863
  action,
@@ -23810,11 +23867,13 @@ var _TinyCloudNode = class _TinyCloudNode {
23810
23867
  }
23811
23868
  /** Raw network authority cannot be covered by a nested encryption space. */
23812
23869
  signedCapabilitySubset(requested, granted) {
23813
- const rawGranted = granted.filter((entry) => this.isEncryptionPermissionEntry(entry)).map((entry) => ({ ...entry, space: "encryption" }));
23814
- const spaceGranted = granted.filter((entry) => !this.isEncryptionPermissionEntry(entry));
23870
+ const rawGranted = granted.filter((entry) => this.isEncryptionPermissionEntry(entry) || this.isRawRevocationPermissionEntry(entry)).map((entry) => this.isEncryptionPermissionEntry(entry) ? { ...entry, space: "encryption" } : entry);
23871
+ const spaceGranted = granted.filter(
23872
+ (entry) => !this.isEncryptionPermissionEntry(entry) && !this.isRawRevocationPermissionEntry(entry)
23873
+ );
23815
23874
  const missing = requested.filter((entry) => {
23816
- const raw = this.isEncryptionPermissionEntry(entry);
23817
- const comparable = raw ? { ...entry, space: "encryption" } : entry;
23875
+ const raw = this.isEncryptionPermissionEntry(entry) || this.isRawRevocationPermissionEntry(entry);
23876
+ const comparable = raw && this.isEncryptionPermissionEntry(entry) ? { ...entry, space: "encryption" } : entry;
23818
23877
  return !(0, import_sdk_core7.isCapabilitySubset)([comparable], raw ? rawGranted : spaceGranted).subset;
23819
23878
  });
23820
23879
  return { subset: missing.length === 0, missing };
@@ -23829,10 +23888,11 @@ var _TinyCloudNode = class _TinyCloudNode {
23829
23888
  }
23830
23889
  permissionEntriesToOperations(entries, session) {
23831
23890
  return entries.flatMap((entry) => {
23832
- const spaceId = this.resolvePermissionSpace(entry.space, session);
23891
+ const rawRevocation = this.isRawRevocationPermissionEntry(entry);
23892
+ const spaceId = rawRevocation ? void 0 : this.resolvePermissionSpace(entry.space, session);
23833
23893
  const service = this.shortServiceName(entry.service);
23834
23894
  return entry.actions.map((action) => ({
23835
- ...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
23895
+ ...rawRevocation ? { resource: entry.space } : this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
23836
23896
  service,
23837
23897
  path: entry.path,
23838
23898
  action,
@@ -23911,7 +23971,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23911
23971
  delegatedResourcesForEntries(entries, spaceId) {
23912
23972
  return entries.map((entry) => ({
23913
23973
  service: this.shortServiceName(entry.service),
23914
- space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
23974
+ space: this.isRawRevocationPermissionEntry(entry) ? entry.space : this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
23915
23975
  path: entry.path,
23916
23976
  actions: [...entry.actions],
23917
23977
  ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
@@ -23921,8 +23981,9 @@ var _TinyCloudNode = class _TinyCloudNode {
23921
23981
  const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
23922
23982
  return resources.flatMap((resource) => {
23923
23983
  const service = this.invocationServiceName(resource.service);
23984
+ const rawRevocation = service === "delegation" && resource.space.startsWith("urn:cid:") && resource.path === "";
23924
23985
  return resource.actions.map((action) => ({
23925
- ...this.isEncryptionNetworkOperation(service, resource.path, resource.space) ? { resource: resource.path } : { spaceId: resource.space },
23986
+ ...rawRevocation ? { resource: resource.space } : this.isEncryptionNetworkOperation(service, resource.path, resource.space) ? { resource: resource.path } : { spaceId: resource.space },
23926
23987
  service,
23927
23988
  path: resource.path,
23928
23989
  action,
@@ -23968,7 +24029,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23968
24029
  * @internal
23969
24030
  */
23970
24031
  buildActivationAbilities(delegation) {
23971
- var _a, _b, _c;
24032
+ var _a, _b, _c, _d;
23972
24033
  const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
23973
24034
  const abilities = {};
23974
24035
  const rawAbilities = {};
@@ -23988,13 +24049,18 @@ var _TinyCloudNode = class _TinyCloudNode {
23988
24049
  addActions(rawAbilities[resource.path], resource.actions);
23989
24050
  continue;
23990
24051
  }
24052
+ if (service === "delegation" && resource.space.startsWith("urn:cid:") && resource.path === "") {
24053
+ rawAbilities[_b = resource.space] ?? (rawAbilities[_b] = []);
24054
+ addActions(rawAbilities[resource.space], resource.actions);
24055
+ continue;
24056
+ }
23991
24057
  if (resource.space !== delegation.spaceId) {
23992
24058
  throw new Error(
23993
24059
  `useDelegation: resource targets space '${resource.space}' but the delegation activates space '${delegation.spaceId}'. Multi-space delegations cannot be activated in a single useDelegation call.`
23994
24060
  );
23995
24061
  }
23996
24062
  abilities[service] ?? (abilities[service] = {});
23997
- (_b = abilities[service])[_c = resource.path] ?? (_b[_c] = []);
24063
+ (_c = abilities[service])[_d = resource.path] ?? (_c[_d] = []);
23998
24064
  addActions(abilities[service][resource.path], resource.actions);
23999
24065
  }
24000
24066
  return { abilities, rawAbilities };