@tinycloud/node-sdk 3.1.0-beta.9 → 3.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1201,12 +1201,25 @@ interface TinyCloudNodeConfig {
1201
1201
  * delegation through the legacy wallet-signed SIWE path, which always
1202
1202
  * triggers a wallet prompt. Used for testing, for explicit wallet
1203
1203
  * confirmation flows, and by the legacy `createDelegation` fallback.
1204
+ *
1205
+ * `onPrepared` runs after the delegation is fully built and signed (CID
1206
+ * included) and before it is activated with the host. It receives the same
1207
+ * `PortableDelegation` object that `delegateTo` returns as
1208
+ * `result.delegation`, so callers can durably record the grant before it
1209
+ * goes live. If the hook rejects, the delegation is not activated and
1210
+ * `delegateTo` rejects with the same error. It cannot be combined with
1211
+ * `forceWalletSign`, whose wallet path activates as part of signing.
1204
1212
  */
1205
1213
  interface DelegateToOptions {
1206
1214
  /** Override expiry. ms-format string ("7d", "1h") or raw milliseconds. */
1207
1215
  expiry?: string | number;
1208
1216
  /** Force the wallet-signed SIWE path even if the caps are derivable. Default false. */
1209
1217
  forceWalletSign?: boolean;
1218
+ /**
1219
+ * Called with the prepared delegation before host activation. A rejection
1220
+ * aborts activation and propagates from `delegateTo`.
1221
+ */
1222
+ onPrepared?: (prepared: PortableDelegation) => Promise<void>;
1210
1223
  }
1211
1224
  /**
1212
1225
  * Result of {@link TinyCloudNode.delegateTo}.
@@ -1504,6 +1517,9 @@ declare class TinyCloudNode {
1504
1517
  private resolveBootstrapDecision;
1505
1518
  private readBootstrapCompletionMarker;
1506
1519
  private writeBootstrapCompletionMarker;
1520
+ /**
1521
+ * A marker written before a canonical space was added triggers one bounded repair.
1522
+ */
1507
1523
  private isAcceptedBootstrapCompletionMarker;
1508
1524
  private markerReadIsUnhostedSpace;
1509
1525
  private runAccountBootstrap;
@@ -2250,6 +2266,10 @@ declare class TinyCloudNode {
2250
2266
  * @throws {@link PermissionNotInManifestError} when any requested
2251
2267
  * entry is not a subset of the granted session capabilities and
2252
2268
  * `forceWalletSign` is not set.
2269
+ * @throws when `onPrepared` is combined with `forceWalletSign: true`,
2270
+ * before anything is signed.
2271
+ * @throws the `onPrepared` rejection, unchanged, without activating the
2272
+ * delegation.
2253
2273
  */
2254
2274
  delegateTo(did: string, permissions: PermissionEntry[], options?: DelegateToOptions): Promise<DelegateToResult>;
2255
2275
  /**
@@ -2300,6 +2320,7 @@ declare class TinyCloudNode {
2300
2320
  private shortServiceName;
2301
2321
  private permissionsToAbilities;
2302
2322
  private isEncryptionPermissionEntry;
2323
+ private isRawRevocationPermissionEntry;
2303
2324
  private permissionsToRawAbilities;
2304
2325
  private permissionOperations;
2305
2326
  /** Raw network authority cannot be covered by a nested encryption space. */
@@ -1201,12 +1201,25 @@ interface TinyCloudNodeConfig {
1201
1201
  * delegation through the legacy wallet-signed SIWE path, which always
1202
1202
  * triggers a wallet prompt. Used for testing, for explicit wallet
1203
1203
  * confirmation flows, and by the legacy `createDelegation` fallback.
1204
+ *
1205
+ * `onPrepared` runs after the delegation is fully built and signed (CID
1206
+ * included) and before it is activated with the host. It receives the same
1207
+ * `PortableDelegation` object that `delegateTo` returns as
1208
+ * `result.delegation`, so callers can durably record the grant before it
1209
+ * goes live. If the hook rejects, the delegation is not activated and
1210
+ * `delegateTo` rejects with the same error. It cannot be combined with
1211
+ * `forceWalletSign`, whose wallet path activates as part of signing.
1204
1212
  */
1205
1213
  interface DelegateToOptions {
1206
1214
  /** Override expiry. ms-format string ("7d", "1h") or raw milliseconds. */
1207
1215
  expiry?: string | number;
1208
1216
  /** Force the wallet-signed SIWE path even if the caps are derivable. Default false. */
1209
1217
  forceWalletSign?: boolean;
1218
+ /**
1219
+ * Called with the prepared delegation before host activation. A rejection
1220
+ * aborts activation and propagates from `delegateTo`.
1221
+ */
1222
+ onPrepared?: (prepared: PortableDelegation) => Promise<void>;
1210
1223
  }
1211
1224
  /**
1212
1225
  * Result of {@link TinyCloudNode.delegateTo}.
@@ -1504,6 +1517,9 @@ declare class TinyCloudNode {
1504
1517
  private resolveBootstrapDecision;
1505
1518
  private readBootstrapCompletionMarker;
1506
1519
  private writeBootstrapCompletionMarker;
1520
+ /**
1521
+ * A marker written before a canonical space was added triggers one bounded repair.
1522
+ */
1507
1523
  private isAcceptedBootstrapCompletionMarker;
1508
1524
  private markerReadIsUnhostedSpace;
1509
1525
  private runAccountBootstrap;
@@ -2250,6 +2266,10 @@ declare class TinyCloudNode {
2250
2266
  * @throws {@link PermissionNotInManifestError} when any requested
2251
2267
  * entry is not a subset of the granted session capabilities and
2252
2268
  * `forceWalletSign` is not set.
2269
+ * @throws when `onPrepared` is combined with `forceWalletSign: true`,
2270
+ * before anything is signed.
2271
+ * @throws the `onPrepared` rejection, unchanged, without activating the
2272
+ * delegation.
2253
2273
  */
2254
2274
  delegateTo(did: string, permissions: PermissionEntry[], options?: DelegateToOptions): Promise<DelegateToResult>;
2255
2275
  /**
@@ -2300,6 +2320,7 @@ declare class TinyCloudNode {
2300
2320
  private shortServiceName;
2301
2321
  private permissionsToAbilities;
2302
2322
  private isEncryptionPermissionEntry;
2323
+ private isRawRevocationPermissionEntry;
2303
2324
  private permissionsToRawAbilities;
2304
2325
  private permissionOperations;
2305
2326
  /** Raw network authority cannot be covered by a nested encryption space. */
package/dist/core.cjs CHANGED
@@ -2699,15 +2699,21 @@ var _TinyCloudNode = class _TinyCloudNode {
2699
2699
  if (!this.wasmBindings.invokeAny) {
2700
2700
  throw new Error("WASM binding does not support invokeAny");
2701
2701
  }
2702
- const operations = entries.flatMap((entry) => {
2702
+ const routedEntries = entries;
2703
+ const operations = routedEntries.flatMap((entry) => {
2703
2704
  const operation = this.operationFromInvokeAnyEntry(entry);
2704
2705
  return operation ? [operation] : [];
2705
2706
  });
2706
- const grant = this.findGrantForOperations(operations);
2707
- const invocationSession = !grant || grant.provenance === "primary" ? session : grant.session;
2708
- const caveatPreservingEntries = grant ? entries.map((entry) => {
2707
+ const authorityCid = routedEntries.length === 1 ? routedEntries[0]?.revokeAuthorityCid : void 0;
2708
+ const explicitGrant = authorityCid === void 0 ? void 0 : this.runtimePermissionGrants.find((grant2) => grant2.delegation.cid === authorityCid);
2709
+ if (authorityCid !== void 0 && !explicitGrant) {
2710
+ throw new Error(`Explicit revocation authority ${authorityCid} is not installed.`);
2711
+ }
2712
+ const grant = explicitGrant ?? this.findGrantForOperations(operations);
2713
+ const invocationSession = explicitGrant ? explicitGrant.session : !grant || grant.provenance === "primary" ? session : grant.session;
2714
+ const caveatPreservingEntries = routedEntries.map(({ revokeAuthorityCid: _cid, ...entry }) => {
2709
2715
  const requested = this.operationFromInvokeAnyEntry(entry);
2710
- const granted = requested && grant.operations.find(
2716
+ const granted = grant && requested && grant.operations.find(
2711
2717
  (candidate) => this.operationCovers(candidate, requested)
2712
2718
  );
2713
2719
  if (!granted?.caveats?.length) {
@@ -2717,7 +2723,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2717
2723
  throw new Error("Invocation caveats do not match signed ReCap authority.");
2718
2724
  }
2719
2725
  return { ...entry, caveats: cloneRecapCaveats(granted.caveats) };
2720
- }) : entries;
2726
+ });
2721
2727
  return this.wasmBindings.invokeAny(invocationSession, caveatPreservingEntries, facts);
2722
2728
  };
2723
2729
  this.explicitHost = config.host;
@@ -3182,12 +3188,13 @@ var _TinyCloudNode = class _TinyCloudNode {
3182
3188
  if (!auth) {
3183
3189
  throw new Error("Account bootstrap requires an active wallet session");
3184
3190
  }
3191
+ const accountSpaceId = this.ownedSpaceId(import_sdk_core8.ACCOUNT_REGISTRY_SPACE);
3185
3192
  const skipped = auth.lastActivationSkippedSpaceIds;
3193
+ if (skipped.includes(accountSpaceId)) return { action: "run", mode: "fresh" };
3186
3194
  if (skipped.some((spaceId) => enshrinedSpaceIds.has(spaceId))) {
3187
- return { action: "run", mode: "fresh" };
3195
+ return { action: "run", mode: "repair" };
3188
3196
  }
3189
3197
  try {
3190
- const accountSpaceId = this.ownedSpaceId(import_sdk_core8.ACCOUNT_REGISTRY_SPACE);
3191
3198
  const markerPermission = {
3192
3199
  service: "tinycloud.kv",
3193
3200
  space: accountSpaceId,
@@ -3199,7 +3206,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3199
3206
  }
3200
3207
  const marker = await this.readBootstrapCompletionMarker();
3201
3208
  if (marker.ok) {
3202
- if (this.isAcceptedBootstrapCompletionMarker(marker.data.data)) {
3209
+ if (this.isAcceptedBootstrapCompletionMarker(marker.data.data, steps.map((step) => step.id))) {
3203
3210
  return { action: "skip" };
3204
3211
  }
3205
3212
  return { action: "run", mode: "repair" };
@@ -3249,12 +3256,16 @@ var _TinyCloudNode = class _TinyCloudNode {
3249
3256
  );
3250
3257
  }
3251
3258
  }
3252
- isAcceptedBootstrapCompletionMarker(value) {
3259
+ /**
3260
+ * A marker written before a canonical space was added triggers one bounded repair.
3261
+ */
3262
+ isAcceptedBootstrapCompletionMarker(value, canonicalStepIds) {
3253
3263
  if (typeof value !== "object" || value === null) return false;
3254
3264
  const entries = Object.entries(value);
3255
3265
  const markerValue = (name) => entries.find(([key]) => key === name)?.[1];
3256
3266
  const version = markerValue("v");
3257
- return typeof version === "number" && Number.isInteger(version) && ACCEPTED_MARKER_VERSIONS.some((acceptedVersion) => acceptedVersion === version);
3267
+ const stepIds = markerValue("stepIds");
3268
+ return typeof version === "number" && Number.isInteger(version) && ACCEPTED_MARKER_VERSIONS.some((acceptedVersion) => acceptedVersion === version) && Array.isArray(stepIds) && canonicalStepIds.every((stepId) => stepIds.includes(stepId));
3258
3269
  }
3259
3270
  markerReadIsUnhostedSpace(meta) {
3260
3271
  return meta?.status === 404;
@@ -3341,30 +3352,39 @@ var _TinyCloudNode = class _TinyCloudNode {
3341
3352
  }
3342
3353
  }
3343
3354
  if (step.kind === "seed-spaces") {
3344
- const registered = await this.account.spaces.registerBatch(
3345
- step.spaces.map((space) => ({
3346
- spaceId: space.spaceId,
3347
- name: space.name,
3348
- ownerDid: this.did,
3349
- type: "owned",
3350
- permissions: ["*"],
3351
- status: "active"
3352
- }))
3353
- );
3354
- if (!registered.ok) {
3355
- throw Object.assign(
3356
- new Error(`Failed to seed account spaces: ${registered.error.message}`),
3357
- { cause: registered.error }
3358
- );
3359
- }
3360
- const batchError = registered.data.recoveredFromBatchError;
3361
- if (batchError) {
3362
- warnings.push({
3363
- stepId: step.id,
3364
- kind: "batch-write-reconciled",
3365
- code: batchError.code,
3366
- message: batchError.message
3367
- });
3355
+ const inputs = step.spaces.map((space) => ({
3356
+ spaceId: space.spaceId,
3357
+ name: space.name,
3358
+ ownerDid: this.did,
3359
+ type: "owned",
3360
+ permissions: ["*"],
3361
+ status: "active"
3362
+ }));
3363
+ if (mode === "fresh") {
3364
+ const registered = await this.account.spaces.registerBatch(inputs);
3365
+ if (!registered.ok) {
3366
+ throw Object.assign(
3367
+ new Error(`Failed to seed account spaces: ${registered.error.message}`),
3368
+ { cause: registered.error }
3369
+ );
3370
+ }
3371
+ const batchError = registered.data.recoveredFromBatchError;
3372
+ if (batchError) {
3373
+ warnings.push({
3374
+ stepId: step.id,
3375
+ kind: "batch-write-reconciled",
3376
+ code: batchError.code,
3377
+ message: batchError.message
3378
+ });
3379
+ }
3380
+ } else {
3381
+ const ensured = await this.account.spaces.registerMissing(inputs);
3382
+ if (!ensured.ok) {
3383
+ throw Object.assign(
3384
+ new Error(`Failed to seed account spaces: ${ensured.error.message}`),
3385
+ { cause: ensured.error }
3386
+ );
3387
+ }
3368
3388
  }
3369
3389
  }
3370
3390
  if (step.kind === "seed-applications") {
@@ -4049,6 +4069,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4049
4069
  stagedSessionExpiry,
4050
4070
  stagedRecap
4051
4071
  );
4072
+ const stagedAccountSpaceId = stagedAddress ? this.wasmBindings.makeSpaceId(stagedAddress, stagedChainId, import_sdk_core8.ACCOUNT_REGISTRY_SPACE) : void 0;
4052
4073
  const stagedGraph = this.stageRestoredServiceGraph({
4053
4074
  host: stagedHost,
4054
4075
  manager: stagedManager,
@@ -4057,6 +4078,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4057
4078
  nodeDid: stagedNodeDid,
4058
4079
  address: stagedAddress,
4059
4080
  chainId: stagedChainId,
4081
+ accountSpaceId: stagedAccountSpaceId,
4060
4082
  tinyCloudSession: stagedTcSession,
4061
4083
  sessionExpiry: stagedSessionExpiry,
4062
4084
  recap: stagedRecap
@@ -4222,6 +4244,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4222
4244
  }
4223
4245
  const delegationManager = new import_sdk_core8.DelegationManager({
4224
4246
  hosts: [input.host],
4247
+ accountSpaceId: input.accountSpaceId,
4225
4248
  session: input.serviceSession,
4226
4249
  invoke: graph.invoke,
4227
4250
  invokeAny: graph.invokeAny,
@@ -5926,6 +5949,14 @@ var _TinyCloudNode = class _TinyCloudNode {
5926
5949
  delegation.delegationHeader
5927
5950
  );
5928
5951
  if (!activateResult.success) {
5952
+ if (this.operationsFromDelegation(delegation).some(
5953
+ (operation) => operation.resource?.startsWith("urn:cid:") === true && operation.service === "delegation" && operation.action === "tinycloud.delegation/revoke"
5954
+ ) && activateResult.status === 401 && /Cannot find parent delegation/i.test(activateResult.error ?? "")) {
5955
+ throw Object.assign(
5956
+ new Error("The host cannot activate a root raw-CID revoke authority without a parent delegation."),
5957
+ { code: "RAW_RECAP_RESOURCE_UNSUPPORTED", cause: activateResult }
5958
+ );
5959
+ }
5929
5960
  throw Object.assign(
5930
5961
  new Error(`Failed to activate runtime permission delegation: ${describeHostFailure2(activateResult)}`),
5931
5962
  { cause: activateResult }
@@ -5973,10 +6004,10 @@ var _TinyCloudNode = class _TinyCloudNode {
5973
6004
  }
5974
6005
  this.assertDelegationCaveatsPreservable(expanded);
5975
6006
  const rawEntries = expanded.filter(
5976
- (entry) => this.isEncryptionPermissionEntry(entry)
6007
+ (entry) => this.isEncryptionPermissionEntry(entry) || this.isRawRevocationPermissionEntry(entry)
5977
6008
  );
5978
6009
  const spaceEntries = expanded.filter(
5979
- (entry) => !this.isEncryptionPermissionEntry(entry)
6010
+ (entry) => !this.isEncryptionPermissionEntry(entry) && !this.isRawRevocationPermissionEntry(entry)
5980
6011
  );
5981
6012
  const bySpace = /* @__PURE__ */ new Map();
5982
6013
  for (const entry of spaceEntries) {
@@ -6003,17 +6034,29 @@ var _TinyCloudNode = class _TinyCloudNode {
6003
6034
  }
6004
6035
  const delegatedEntries = [...entries, ...rawForDelegation];
6005
6036
  const abilities = this.permissionsToAbilities(entries);
6006
- const prepared = this.wasmBindings.prepareSession({
6007
- abilities,
6008
- ...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
6009
- address: this.wasmBindings.ensureEip55(session.address),
6010
- chainId: session.chainId,
6011
- domain: this.siweDomain,
6012
- issuedAt: now.toISOString(),
6013
- expirationTime: expiresAt.toISOString(),
6014
- spaceId,
6015
- jwk: session.jwk
6016
- });
6037
+ const prepared = (() => {
6038
+ try {
6039
+ return this.wasmBindings.prepareSession({
6040
+ abilities,
6041
+ ...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
6042
+ address: this.wasmBindings.ensureEip55(session.address),
6043
+ chainId: session.chainId,
6044
+ domain: this.siweDomain,
6045
+ issuedAt: now.toISOString(),
6046
+ expirationTime: expiresAt.toISOString(),
6047
+ spaceId,
6048
+ jwk: session.jwk
6049
+ });
6050
+ } catch (error) {
6051
+ if (rawForDelegation.some((entry) => this.isRawRevocationPermissionEntry(entry)) && /invalid ReCap resource URI/i.test(error instanceof Error ? error.message : String(error))) {
6052
+ throw Object.assign(
6053
+ new Error("This WASM build cannot encode raw urn:cid ReCap resources."),
6054
+ { code: "RAW_RECAP_RESOURCE_UNSUPPORTED", cause: error }
6055
+ );
6056
+ }
6057
+ throw error;
6058
+ }
6059
+ })();
6017
6060
  const signature = await this.signer.signMessage(prepared.siwe);
6018
6061
  const delegatedSession = this.wasmBindings.completeSessionSetup({
6019
6062
  ...prepared,
@@ -6024,6 +6067,12 @@ var _TinyCloudNode = class _TinyCloudNode {
6024
6067
  delegatedSession.delegationHeader
6025
6068
  );
6026
6069
  if (!activateResult.success) {
6070
+ if (rawForDelegation.some((entry) => this.isRawRevocationPermissionEntry(entry)) && activateResult.status === 401 && /Cannot find parent delegation/i.test(activateResult.error ?? "")) {
6071
+ throw Object.assign(
6072
+ new Error("The host cannot activate a root raw-CID revoke authority without a parent delegation."),
6073
+ { code: "RAW_RECAP_RESOURCE_UNSUPPORTED", cause: activateResult }
6074
+ );
6075
+ }
6027
6076
  throw Object.assign(
6028
6077
  new Error(`Failed to activate runtime permission delegation: ${describeHostFailure2(activateResult)}`),
6029
6078
  { cause: activateResult }
@@ -6376,8 +6425,18 @@ var _TinyCloudNode = class _TinyCloudNode {
6376
6425
  * @throws {@link PermissionNotInManifestError} when any requested
6377
6426
  * entry is not a subset of the granted session capabilities and
6378
6427
  * `forceWalletSign` is not set.
6428
+ * @throws when `onPrepared` is combined with `forceWalletSign: true`,
6429
+ * before anything is signed.
6430
+ * @throws the `onPrepared` rejection, unchanged, without activating the
6431
+ * delegation.
6379
6432
  */
6380
6433
  async delegateTo(did, permissions, options) {
6434
+ const onPrepared = options?.onPrepared;
6435
+ if (onPrepared && options?.forceWalletSign) {
6436
+ throw new Error(
6437
+ "delegateTo: onPrepared cannot be combined with forceWalletSign=true; the wallet-signed path activates the delegation before it could be handed to onPrepared."
6438
+ );
6439
+ }
6381
6440
  const session = this.currentTinyCloudSession();
6382
6441
  if (!session) {
6383
6442
  throw new import_sdk_core8.SessionExpiredError(/* @__PURE__ */ new Date(0));
@@ -6436,7 +6495,8 @@ var _TinyCloudNode = class _TinyCloudNode {
6436
6495
  expandedEntries,
6437
6496
  runtimeExpiration,
6438
6497
  runtimeGrant,
6439
- runtimeOperations
6498
+ runtimeOperations,
6499
+ onPrepared
6440
6500
  );
6441
6501
  return { delegation: delegation2, prompted: false };
6442
6502
  }
@@ -6446,7 +6506,8 @@ var _TinyCloudNode = class _TinyCloudNode {
6446
6506
  did,
6447
6507
  expandedEntries,
6448
6508
  effectiveExpiration,
6449
- session
6509
+ session,
6510
+ onPrepared
6450
6511
  );
6451
6512
  return { delegation, prompted: false };
6452
6513
  }
@@ -6500,7 +6561,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6500
6561
  *
6501
6562
  * @internal
6502
6563
  */
6503
- async createDelegationViaWasmPath(did, entries, expirationTime, session) {
6564
+ async createDelegationViaWasmPath(did, entries, expirationTime, session, onPrepared) {
6504
6565
  if (entries.length === 0) {
6505
6566
  throw new Error(
6506
6567
  "createDelegationViaWasmPath requires a non-empty entries array"
@@ -6563,17 +6624,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6563
6624
  });
6564
6625
  const primary = result.resources[0];
6565
6626
  const delegationHeader = { Authorization: result.delegation };
6566
- const activateResult = await (0, import_sdk_core8.activateSessionWithHost)(
6567
- this.config.host,
6568
- delegationHeader
6569
- );
6570
- if (!activateResult.success) {
6571
- throw Object.assign(
6572
- new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
6573
- { cause: activateResult }
6574
- );
6575
- }
6576
- return {
6627
+ const delegation = {
6577
6628
  cid: result.cid,
6578
6629
  delegationHeader,
6579
6630
  spaceId,
@@ -6587,8 +6638,22 @@ var _TinyCloudNode = class _TinyCloudNode {
6587
6638
  chainId: session.chainId,
6588
6639
  host: this.config.host
6589
6640
  };
6641
+ if (onPrepared) {
6642
+ await onPrepared(delegation);
6643
+ }
6644
+ const activateResult = await (0, import_sdk_core8.activateSessionWithHost)(
6645
+ this.config.host,
6646
+ delegationHeader
6647
+ );
6648
+ if (!activateResult.success) {
6649
+ throw Object.assign(
6650
+ new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
6651
+ { cause: activateResult }
6652
+ );
6653
+ }
6654
+ return delegation;
6590
6655
  }
6591
- async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant, requestedOperations) {
6656
+ async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant, requestedOperations, onPrepared) {
6592
6657
  this.assertRuntimeGrantCaveatsPreservable(entries, requestedOperations, grant);
6593
6658
  this.assertDelegationCaveatsPreservable(entries);
6594
6659
  const result = this.createDelegationWrapper({
@@ -6601,17 +6666,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6601
6666
  const primary = result.resources[0];
6602
6667
  const delegationHeader = { Authorization: result.delegation };
6603
6668
  const targetHost = grant.delegation.host ?? this.config.host;
6604
- const activateResult = await (0, import_sdk_core8.activateSessionWithHost)(
6605
- targetHost,
6606
- delegationHeader
6607
- );
6608
- if (!activateResult.success) {
6609
- throw Object.assign(
6610
- new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
6611
- { cause: activateResult }
6612
- );
6613
- }
6614
- return {
6669
+ const delegation = {
6615
6670
  cid: result.cid,
6616
6671
  delegationHeader,
6617
6672
  spaceId: grant.session.spaceId,
@@ -6625,6 +6680,20 @@ var _TinyCloudNode = class _TinyCloudNode {
6625
6680
  chainId: grant.delegation.chainId,
6626
6681
  host: targetHost
6627
6682
  };
6683
+ if (onPrepared) {
6684
+ await onPrepared(delegation);
6685
+ }
6686
+ const activateResult = await (0, import_sdk_core8.activateSessionWithHost)(
6687
+ targetHost,
6688
+ delegationHeader
6689
+ );
6690
+ if (!activateResult.success) {
6691
+ throw Object.assign(
6692
+ new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
6693
+ { cause: activateResult }
6694
+ );
6695
+ }
6696
+ return delegation;
6628
6697
  }
6629
6698
  resolvePermissionSpace(space, session) {
6630
6699
  if (space === void 0) {
@@ -6725,6 +6794,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6725
6794
  permissionsToAbilities(entries) {
6726
6795
  const abilities = {};
6727
6796
  for (const entry of entries) {
6797
+ if (this.isRawRevocationPermissionEntry(entry)) continue;
6728
6798
  const service = this.shortServiceName(entry.service);
6729
6799
  abilities[service] ?? (abilities[service] = {});
6730
6800
  const existing = abilities[service][entry.path] ?? [];
@@ -6742,9 +6812,16 @@ var _TinyCloudNode = class _TinyCloudNode {
6742
6812
  isEncryptionPermissionEntry(entry) {
6743
6813
  return entry.service === import_sdk_core8.ENCRYPTION_PERMISSION_SERVICE && this.isEncryptionNetworkOperation("encryption", entry.path, entry.space);
6744
6814
  }
6815
+ isRawRevocationPermissionEntry(entry) {
6816
+ return entry.service === "tinycloud.delegation" && entry.path === "" && entry.space?.startsWith("urn:cid:") === true && entry.actions.includes("tinycloud.delegation/revoke");
6817
+ }
6745
6818
  permissionsToRawAbilities(entries) {
6746
6819
  const rawAbilities = {};
6747
6820
  for (const entry of entries) {
6821
+ if (this.isRawRevocationPermissionEntry(entry)) {
6822
+ rawAbilities[entry.space] = [...new Set(entry.actions)];
6823
+ continue;
6824
+ }
6748
6825
  if (!this.isEncryptionPermissionEntry(entry)) {
6749
6826
  continue;
6750
6827
  }
@@ -6764,7 +6841,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6764
6841
  return entries.flatMap((entry) => {
6765
6842
  const service = this.shortServiceName(entry.service);
6766
6843
  return entry.actions.map((action) => ({
6767
- ...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
6844
+ ...this.isRawRevocationPermissionEntry(entry) ? { resource: entry.space } : this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
6768
6845
  service,
6769
6846
  path: entry.path,
6770
6847
  action,
@@ -6774,11 +6851,13 @@ var _TinyCloudNode = class _TinyCloudNode {
6774
6851
  }
6775
6852
  /** Raw network authority cannot be covered by a nested encryption space. */
6776
6853
  signedCapabilitySubset(requested, granted) {
6777
- const rawGranted = granted.filter((entry) => this.isEncryptionPermissionEntry(entry)).map((entry) => ({ ...entry, space: "encryption" }));
6778
- const spaceGranted = granted.filter((entry) => !this.isEncryptionPermissionEntry(entry));
6854
+ const rawGranted = granted.filter((entry) => this.isEncryptionPermissionEntry(entry) || this.isRawRevocationPermissionEntry(entry)).map((entry) => this.isEncryptionPermissionEntry(entry) ? { ...entry, space: "encryption" } : entry);
6855
+ const spaceGranted = granted.filter(
6856
+ (entry) => !this.isEncryptionPermissionEntry(entry) && !this.isRawRevocationPermissionEntry(entry)
6857
+ );
6779
6858
  const missing = requested.filter((entry) => {
6780
- const raw = this.isEncryptionPermissionEntry(entry);
6781
- const comparable = raw ? { ...entry, space: "encryption" } : entry;
6859
+ const raw = this.isEncryptionPermissionEntry(entry) || this.isRawRevocationPermissionEntry(entry);
6860
+ const comparable = raw && this.isEncryptionPermissionEntry(entry) ? { ...entry, space: "encryption" } : entry;
6782
6861
  return !(0, import_sdk_core8.isCapabilitySubset)([comparable], raw ? rawGranted : spaceGranted).subset;
6783
6862
  });
6784
6863
  return { subset: missing.length === 0, missing };
@@ -6793,10 +6872,11 @@ var _TinyCloudNode = class _TinyCloudNode {
6793
6872
  }
6794
6873
  permissionEntriesToOperations(entries, session) {
6795
6874
  return entries.flatMap((entry) => {
6796
- const spaceId = this.resolvePermissionSpace(entry.space, session);
6875
+ const rawRevocation = this.isRawRevocationPermissionEntry(entry);
6876
+ const spaceId = rawRevocation ? void 0 : this.resolvePermissionSpace(entry.space, session);
6797
6877
  const service = this.shortServiceName(entry.service);
6798
6878
  return entry.actions.map((action) => ({
6799
- ...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
6879
+ ...rawRevocation ? { resource: entry.space } : this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
6800
6880
  service,
6801
6881
  path: entry.path,
6802
6882
  action,
@@ -6875,7 +6955,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6875
6955
  delegatedResourcesForEntries(entries, spaceId) {
6876
6956
  return entries.map((entry) => ({
6877
6957
  service: this.shortServiceName(entry.service),
6878
- space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
6958
+ space: this.isRawRevocationPermissionEntry(entry) ? entry.space : this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
6879
6959
  path: entry.path,
6880
6960
  actions: [...entry.actions],
6881
6961
  ...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
@@ -6885,8 +6965,9 @@ var _TinyCloudNode = class _TinyCloudNode {
6885
6965
  const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
6886
6966
  return resources.flatMap((resource) => {
6887
6967
  const service = this.invocationServiceName(resource.service);
6968
+ const rawRevocation = service === "delegation" && resource.space.startsWith("urn:cid:") && resource.path === "";
6888
6969
  return resource.actions.map((action) => ({
6889
- ...this.isEncryptionNetworkOperation(service, resource.path, resource.space) ? { resource: resource.path } : { spaceId: resource.space },
6970
+ ...rawRevocation ? { resource: resource.space } : this.isEncryptionNetworkOperation(service, resource.path, resource.space) ? { resource: resource.path } : { spaceId: resource.space },
6890
6971
  service,
6891
6972
  path: resource.path,
6892
6973
  action,
@@ -6932,7 +7013,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6932
7013
  * @internal
6933
7014
  */
6934
7015
  buildActivationAbilities(delegation) {
6935
- var _a, _b, _c;
7016
+ var _a, _b, _c, _d;
6936
7017
  const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
6937
7018
  const abilities = {};
6938
7019
  const rawAbilities = {};
@@ -6952,13 +7033,18 @@ var _TinyCloudNode = class _TinyCloudNode {
6952
7033
  addActions(rawAbilities[resource.path], resource.actions);
6953
7034
  continue;
6954
7035
  }
7036
+ if (service === "delegation" && resource.space.startsWith("urn:cid:") && resource.path === "") {
7037
+ rawAbilities[_b = resource.space] ?? (rawAbilities[_b] = []);
7038
+ addActions(rawAbilities[resource.space], resource.actions);
7039
+ continue;
7040
+ }
6955
7041
  if (resource.space !== delegation.spaceId) {
6956
7042
  throw new Error(
6957
7043
  `useDelegation: resource targets space '${resource.space}' but the delegation activates space '${delegation.spaceId}'. Multi-space delegations cannot be activated in a single useDelegation call.`
6958
7044
  );
6959
7045
  }
6960
7046
  abilities[service] ?? (abilities[service] = {});
6961
- (_b = abilities[service])[_c = resource.path] ?? (_b[_c] = []);
7047
+ (_c = abilities[service])[_d = resource.path] ?? (_c[_d] = []);
6962
7048
  addActions(abilities[service][resource.path], resource.actions);
6963
7049
  }
6964
7050
  return { abilities, rawAbilities };