@tinycloud/node-sdk 3.0.1-beta.0 → 3.1.0-beta.10
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-DufYfxi-.d.cts → core-CC7uDKZP.d.cts} +84 -7
- package/dist/{core-DufYfxi-.d.ts → core-CC7uDKZP.d.ts} +84 -7
- package/dist/core.cjs +344 -120
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +351 -121
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +402 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +409 -154
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/core.cjs
CHANGED
|
@@ -336,6 +336,10 @@ var defaultSignStrategy = { type: "auto-sign" };
|
|
|
336
336
|
// src/authorization/NodeUserAuthorization.ts
|
|
337
337
|
var DECRYPT_ACTION = import_sdk_core3.ENCRYPTION.DECRYPT;
|
|
338
338
|
var NETWORK_CREATE_ACTION = import_sdk_core3.ENCRYPTION.NETWORK_CREATE;
|
|
339
|
+
function describeHostFailure(result) {
|
|
340
|
+
const text = result.error?.trim();
|
|
341
|
+
return text ? `${result.status} - ${text}` : `${result.status}`;
|
|
342
|
+
}
|
|
339
343
|
function canonicalizeOpenKeyManifestJson(value) {
|
|
340
344
|
const json = JSON.stringify(value);
|
|
341
345
|
if (json === void 0) {
|
|
@@ -800,21 +804,28 @@ var NodeUserAuthorization = class {
|
|
|
800
804
|
});
|
|
801
805
|
const signature = await this.signMessage(siwe, purpose);
|
|
802
806
|
const headers = this.wasm.siweToDelegationHeaders({ siwe, signature });
|
|
803
|
-
|
|
804
|
-
return result.success;
|
|
807
|
+
return (0, import_sdk_core3.submitHostDelegation)(host, headers);
|
|
805
808
|
}
|
|
806
809
|
/**
|
|
807
810
|
* Create a specific space on the server via host delegation.
|
|
808
811
|
* Used for lazy creation of additional spaces (e.g., public).
|
|
809
812
|
*/
|
|
810
813
|
async hostPublicSpace(spaceId) {
|
|
811
|
-
return this.hostSpace(spaceId);
|
|
814
|
+
return (await this.hostSpace(spaceId)).success;
|
|
812
815
|
}
|
|
813
816
|
/**
|
|
814
817
|
* Create a specific owned space on the server via host delegation.
|
|
815
818
|
* Used by manifest registry setup for the account space.
|
|
816
819
|
*/
|
|
817
820
|
async hostOwnedSpace(spaceId, purpose) {
|
|
821
|
+
return (await this.hostOwnedSpaceResult(spaceId, purpose)).success;
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* {@link hostOwnedSpace} with the full host-delegation result, so a failure
|
|
825
|
+
* keeps its HTTP status (e.g. a typed 401/403) instead of collapsing to
|
|
826
|
+
* `false`.
|
|
827
|
+
*/
|
|
828
|
+
async hostOwnedSpaceResult(spaceId, purpose) {
|
|
818
829
|
return this.hostSpace(spaceId, purpose);
|
|
819
830
|
}
|
|
820
831
|
/**
|
|
@@ -858,9 +869,11 @@ var NodeUserAuthorization = class {
|
|
|
858
869
|
}
|
|
859
870
|
try {
|
|
860
871
|
const created = await this.hostSpace();
|
|
861
|
-
if (!created) {
|
|
862
|
-
const err =
|
|
863
|
-
|
|
872
|
+
if (!created.success) {
|
|
873
|
+
const err = Object.assign(
|
|
874
|
+
new Error(`Failed to create space ${primarySpaceId}: ${describeHostFailure(created)}`),
|
|
875
|
+
{ cause: created }
|
|
876
|
+
);
|
|
864
877
|
throw err;
|
|
865
878
|
}
|
|
866
879
|
} catch (error) {
|
|
@@ -876,8 +889,9 @@ var NodeUserAuthorization = class {
|
|
|
876
889
|
this._tinyCloudSession.delegationHeader
|
|
877
890
|
);
|
|
878
891
|
if (!retryResult.success) {
|
|
879
|
-
const err =
|
|
880
|
-
`Failed to activate session after creating space: ${retryResult
|
|
892
|
+
const err = Object.assign(
|
|
893
|
+
new Error(`Failed to activate session after creating space: ${describeHostFailure(retryResult)}`),
|
|
894
|
+
{ cause: retryResult }
|
|
881
895
|
);
|
|
882
896
|
handler.onSpaceCreationFailed?.(creationContext, err);
|
|
883
897
|
throw err;
|
|
@@ -895,9 +909,11 @@ var NodeUserAuthorization = class {
|
|
|
895
909
|
}
|
|
896
910
|
try {
|
|
897
911
|
const created = await this.hostSpace();
|
|
898
|
-
if (!created) {
|
|
899
|
-
const err =
|
|
900
|
-
|
|
912
|
+
if (!created.success) {
|
|
913
|
+
const err = Object.assign(
|
|
914
|
+
new Error(`Failed to create space ${primarySpaceId}: ${describeHostFailure(created)}`),
|
|
915
|
+
{ cause: created }
|
|
916
|
+
);
|
|
901
917
|
throw err;
|
|
902
918
|
}
|
|
903
919
|
} catch (error) {
|
|
@@ -913,8 +929,9 @@ var NodeUserAuthorization = class {
|
|
|
913
929
|
this._tinyCloudSession.delegationHeader
|
|
914
930
|
);
|
|
915
931
|
if (!retryResult.success) {
|
|
916
|
-
const err =
|
|
917
|
-
`Failed to activate session after creating space: ${retryResult
|
|
932
|
+
const err = Object.assign(
|
|
933
|
+
new Error(`Failed to activate session after creating space: ${describeHostFailure(retryResult)}`),
|
|
934
|
+
{ cause: retryResult }
|
|
918
935
|
);
|
|
919
936
|
handler.onSpaceCreationFailed?.(creationContext, err);
|
|
920
937
|
throw err;
|
|
@@ -922,7 +939,10 @@ var NodeUserAuthorization = class {
|
|
|
922
939
|
handler.onSpaceCreated?.(creationContext);
|
|
923
940
|
return;
|
|
924
941
|
}
|
|
925
|
-
throw
|
|
942
|
+
throw Object.assign(
|
|
943
|
+
new Error(`Failed to activate session: ${describeHostFailure(result)}`),
|
|
944
|
+
{ cause: result }
|
|
945
|
+
);
|
|
926
946
|
}
|
|
927
947
|
recordActivationSkippedSpaces(result, primarySpaceId) {
|
|
928
948
|
if (result.success) {
|
|
@@ -1712,14 +1732,14 @@ var NodeUserAuthorization = class {
|
|
|
1712
1732
|
* Request signature via event emitter with timeout.
|
|
1713
1733
|
*/
|
|
1714
1734
|
requestSignatureViaEmitter(request, emitter, timeout) {
|
|
1715
|
-
return new Promise((resolve,
|
|
1735
|
+
return new Promise((resolve, reject2) => {
|
|
1716
1736
|
const timeoutId = setTimeout(() => {
|
|
1717
|
-
|
|
1737
|
+
reject2(new Error("Sign request timed out"));
|
|
1718
1738
|
}, timeout);
|
|
1719
1739
|
const respond = async (response) => {
|
|
1720
1740
|
clearTimeout(timeoutId);
|
|
1721
1741
|
if (!response.approved) {
|
|
1722
|
-
|
|
1742
|
+
reject2(
|
|
1723
1743
|
new Error(response.reason ?? "Sign request rejected via emitter")
|
|
1724
1744
|
);
|
|
1725
1745
|
} else {
|
|
@@ -2302,6 +2322,25 @@ function jsonFromVerifier(value) {
|
|
|
2302
2322
|
function verifierRecapEntries(entries) {
|
|
2303
2323
|
return entries.map((entry) => Array.isArray(entry.caveats) ? { ...entry, caveats: entry.caveats.map((caveat) => jsonFromVerifier(caveat)) } : entry);
|
|
2304
2324
|
}
|
|
2325
|
+
function exactVerifiedRecap(verified) {
|
|
2326
|
+
const exactRecap = Array.isArray(verified.verifiedRecap) ? verifierRecapEntries(verified.verifiedRecap) : verified.verifiedRecap;
|
|
2327
|
+
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
2328
|
+
(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(
|
|
2329
|
+
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
2330
|
+
)
|
|
2331
|
+
)) {
|
|
2332
|
+
throw new UnsupportedSessionRestoreError(
|
|
2333
|
+
"it cannot reconstruct caveat-preserving persisted ReCap authority"
|
|
2334
|
+
);
|
|
2335
|
+
}
|
|
2336
|
+
return exactRecap.map((entry) => ({
|
|
2337
|
+
service: entry.service,
|
|
2338
|
+
space: entry.space,
|
|
2339
|
+
path: entry.path,
|
|
2340
|
+
actions: [...entry.actions],
|
|
2341
|
+
caveats: cloneRecapCaveats(entry.caveats)
|
|
2342
|
+
}));
|
|
2343
|
+
}
|
|
2305
2344
|
var ServiceGraphLifetime = class {
|
|
2306
2345
|
constructor(invokeFn, invokeAnyFn, fetchFn) {
|
|
2307
2346
|
this.invokeFn = invokeFn;
|
|
@@ -2397,6 +2436,10 @@ function persistedExpiry(value) {
|
|
|
2397
2436
|
function sameInstant(left, right) {
|
|
2398
2437
|
return left.getTime() === right.getTime();
|
|
2399
2438
|
}
|
|
2439
|
+
function describeHostFailure2(result) {
|
|
2440
|
+
const text = result.error?.trim();
|
|
2441
|
+
return text ? `${result.status} - ${text}` : `${result.status}`;
|
|
2442
|
+
}
|
|
2400
2443
|
function ownerDelegationPermissions(params) {
|
|
2401
2444
|
const hasPermissions = Object.prototype.hasOwnProperty.call(params, "permissions");
|
|
2402
2445
|
const hasLegacy = Object.prototype.hasOwnProperty.call(params, "path") || Object.prototype.hasOwnProperty.call(params, "actions");
|
|
@@ -3200,7 +3243,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3200
3243
|
marker
|
|
3201
3244
|
);
|
|
3202
3245
|
if (!written.ok) {
|
|
3203
|
-
throw
|
|
3246
|
+
throw Object.assign(
|
|
3247
|
+
new Error(`Failed to write bootstrap completion marker: ${written.error.message}`),
|
|
3248
|
+
{ cause: written.error }
|
|
3249
|
+
);
|
|
3204
3250
|
}
|
|
3205
3251
|
}
|
|
3206
3252
|
isAcceptedBootstrapCompletionMarker(value) {
|
|
@@ -3246,17 +3292,23 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3246
3292
|
rawAbilities
|
|
3247
3293
|
});
|
|
3248
3294
|
} catch (err) {
|
|
3249
|
-
throw
|
|
3250
|
-
|
|
3295
|
+
throw Object.assign(
|
|
3296
|
+
new Error(
|
|
3297
|
+
`Account bootstrap aborted: signature rejected for space "${step.space}". Cause: ${err instanceof Error ? err.message : String(err)}`
|
|
3298
|
+
),
|
|
3299
|
+
{ cause: err }
|
|
3251
3300
|
);
|
|
3252
3301
|
}
|
|
3253
3302
|
sessions.set(step.space, session);
|
|
3254
3303
|
}
|
|
3255
3304
|
for (const step of steps) {
|
|
3256
3305
|
if (step.kind !== "host") continue;
|
|
3257
|
-
const hosted = await auth.
|
|
3258
|
-
if (!hosted) {
|
|
3259
|
-
throw
|
|
3306
|
+
const hosted = await auth.hostOwnedSpaceResult(step.spaceId, "bootstrap-host");
|
|
3307
|
+
if (!hosted.success) {
|
|
3308
|
+
throw Object.assign(
|
|
3309
|
+
new Error(`Failed to host bootstrap space ${step.spaceId}: ${describeHostFailure2(hosted)}`),
|
|
3310
|
+
{ cause: hosted }
|
|
3311
|
+
);
|
|
3260
3312
|
}
|
|
3261
3313
|
}
|
|
3262
3314
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
@@ -3268,8 +3320,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3268
3320
|
}
|
|
3269
3321
|
const activated = await (0, import_sdk_core8.activateSessionWithHost)(host, session.delegationHeader);
|
|
3270
3322
|
if (!activated.success || activated.skipped?.includes(step.spaceId)) {
|
|
3271
|
-
throw
|
|
3272
|
-
|
|
3323
|
+
throw Object.assign(
|
|
3324
|
+
new Error(
|
|
3325
|
+
`Failed to activate bootstrap session for ${step.spaceId}: ${activated.success ? "space was skipped" : describeHostFailure2(activated)}`
|
|
3326
|
+
),
|
|
3327
|
+
{ cause: activated }
|
|
3273
3328
|
);
|
|
3274
3329
|
}
|
|
3275
3330
|
this.registerBootstrapRuntimeGrant(
|
|
@@ -3282,7 +3337,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3282
3337
|
if (step.kind === "account-index-schema") {
|
|
3283
3338
|
const ensured = await this.account.index.ensure();
|
|
3284
3339
|
if (!ensured.ok) {
|
|
3285
|
-
throw new Error(`Failed to create account index schema: ${ensured.error.message}`);
|
|
3340
|
+
throw Object.assign(new Error(`Failed to create account index schema: ${ensured.error.message}`), { cause: ensured.error });
|
|
3286
3341
|
}
|
|
3287
3342
|
}
|
|
3288
3343
|
if (step.kind === "seed-spaces") {
|
|
@@ -3297,8 +3352,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3297
3352
|
}))
|
|
3298
3353
|
);
|
|
3299
3354
|
if (!registered.ok) {
|
|
3300
|
-
throw
|
|
3301
|
-
`Failed to seed account spaces: ${registered.error.message}`
|
|
3355
|
+
throw Object.assign(
|
|
3356
|
+
new Error(`Failed to seed account spaces: ${registered.error.message}`),
|
|
3357
|
+
{ cause: registered.error }
|
|
3302
3358
|
);
|
|
3303
3359
|
}
|
|
3304
3360
|
const batchError = registered.data.recoveredFromBatchError;
|
|
@@ -3319,7 +3375,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3319
3375
|
{ assumeUnregistered: true }
|
|
3320
3376
|
);
|
|
3321
3377
|
if (!registered.ok) {
|
|
3322
|
-
throw new Error(`Failed to seed bootstrap applications: ${registered.error.message}`);
|
|
3378
|
+
throw Object.assign(new Error(`Failed to seed bootstrap applications: ${registered.error.message}`), { cause: registered.error });
|
|
3323
3379
|
}
|
|
3324
3380
|
}
|
|
3325
3381
|
if (step.kind === "encryption-network-create") {
|
|
@@ -3337,8 +3393,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3337
3393
|
]
|
|
3338
3394
|
});
|
|
3339
3395
|
if (!migrated.ok) {
|
|
3340
|
-
throw
|
|
3341
|
-
`Failed to create secret_records schema: ${migrated.error.message}`
|
|
3396
|
+
throw Object.assign(
|
|
3397
|
+
new Error(`Failed to create secret_records schema: ${migrated.error.message}`),
|
|
3398
|
+
{ cause: migrated.error }
|
|
3342
3399
|
);
|
|
3343
3400
|
}
|
|
3344
3401
|
}
|
|
@@ -3510,8 +3567,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3510
3567
|
await this.ensureOwnedSpaceHostedById(accountSpaceId);
|
|
3511
3568
|
const result = await this.account.applications.register(request.manifests);
|
|
3512
3569
|
if (!result.ok) {
|
|
3513
|
-
throw
|
|
3514
|
-
`Failed to write manifest registry records: ${result.error.message}`
|
|
3570
|
+
throw Object.assign(
|
|
3571
|
+
new Error(`Failed to write manifest registry records: ${result.error.message}`),
|
|
3572
|
+
{ cause: result.error }
|
|
3515
3573
|
);
|
|
3516
3574
|
}
|
|
3517
3575
|
}
|
|
@@ -3536,7 +3594,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3536
3594
|
if (this.currentSessionCanListSpaces()) {
|
|
3537
3595
|
const spaces = await this.account.spaces.syncAccessible();
|
|
3538
3596
|
if (!spaces.ok) {
|
|
3539
|
-
throw
|
|
3597
|
+
throw Object.assign(
|
|
3598
|
+
new Error(`Failed to sync account spaces: ${spaces.error.message}`),
|
|
3599
|
+
{ cause: spaces.error }
|
|
3600
|
+
);
|
|
3540
3601
|
}
|
|
3541
3602
|
}
|
|
3542
3603
|
});
|
|
@@ -3591,14 +3652,20 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3591
3652
|
await task();
|
|
3592
3653
|
return;
|
|
3593
3654
|
} catch (error) {
|
|
3655
|
+
const verdict = (0, import_sdk_core8.authorizationVerdictOf)(error);
|
|
3594
3656
|
const message = error instanceof Error ? error.message : String(error);
|
|
3595
|
-
|
|
3657
|
+
const isAuthorizationVerdict = verdict === void 0 ? /Unauthorized Action|\b(?:401|403)\b/.test(message) : verdict !== "other";
|
|
3658
|
+
if (isAuthorizationVerdict) {
|
|
3596
3659
|
console.warn(
|
|
3597
3660
|
"TinyCloud account registry sync stopped: authorization verdict is not retryable",
|
|
3598
3661
|
error
|
|
3599
3662
|
);
|
|
3600
3663
|
return;
|
|
3601
3664
|
}
|
|
3665
|
+
if ((0, import_sdk_core8.isStorageFullError)(error) || (0, import_sdk_core8.isStorageFullError)(error?.cause) || message.includes(import_sdk_core8.STORAGE_FULL_MESSAGE) || message.includes(import_sdk_core8.STORAGE_WRITE_TOO_LARGE_MESSAGE)) {
|
|
3666
|
+
console.warn("TinyCloud account registry sync stopped: storage is full", error);
|
|
3667
|
+
return;
|
|
3668
|
+
}
|
|
3602
3669
|
lastError = error;
|
|
3603
3670
|
if (attempt < delays.length - 1) {
|
|
3604
3671
|
await new Promise((resolve) => setTimeout(resolve, delays[attempt]));
|
|
@@ -3672,19 +3739,26 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3672
3739
|
return;
|
|
3673
3740
|
}
|
|
3674
3741
|
if (!activation.success && activation.status !== 404) {
|
|
3675
|
-
throw
|
|
3676
|
-
`Failed to check owned space ${spaceId}: ${activation
|
|
3742
|
+
throw Object.assign(
|
|
3743
|
+
new Error(`Failed to check owned space ${spaceId}: ${describeHostFailure2(activation)}`),
|
|
3744
|
+
{ cause: activation }
|
|
3677
3745
|
);
|
|
3678
3746
|
}
|
|
3679
|
-
const created = await this.auth.
|
|
3680
|
-
if (!created) {
|
|
3681
|
-
throw
|
|
3747
|
+
const created = await this.auth.hostOwnedSpaceResult(spaceId);
|
|
3748
|
+
if (!created.success) {
|
|
3749
|
+
throw Object.assign(
|
|
3750
|
+
new Error(`Failed to create owned space ${spaceId}: ${describeHostFailure2(created)}`),
|
|
3751
|
+
{ cause: created }
|
|
3752
|
+
);
|
|
3682
3753
|
}
|
|
3683
3754
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
3684
3755
|
const retry = await (0, import_sdk_core8.activateSessionWithHost)(host, session.delegationHeader);
|
|
3685
3756
|
if (!retry.success || retry.skipped?.includes(spaceId)) {
|
|
3686
|
-
throw
|
|
3687
|
-
|
|
3757
|
+
throw Object.assign(
|
|
3758
|
+
new Error(
|
|
3759
|
+
`Failed to activate session after creating owned space ${spaceId}: ${retry.success ? "space was skipped" : describeHostFailure2(retry)}`
|
|
3760
|
+
),
|
|
3761
|
+
{ cause: retry }
|
|
3688
3762
|
);
|
|
3689
3763
|
}
|
|
3690
3764
|
this.confirmedHostedSpaceIds.add(spaceId);
|
|
@@ -3719,19 +3793,23 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3719
3793
|
if (!host) {
|
|
3720
3794
|
throw new Error("Owned space hosting requires a TinyCloud host");
|
|
3721
3795
|
}
|
|
3722
|
-
const hosted = await this.auth.
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3796
|
+
const hosted = await this.auth.hostOwnedSpaceResult(spaceId);
|
|
3797
|
+
if (!hosted.success) {
|
|
3798
|
+
throw Object.assign(
|
|
3799
|
+
new Error(`Failed to host owned space ${spaceId}: ${describeHostFailure2(hosted)}`),
|
|
3800
|
+
{ cause: hosted }
|
|
3801
|
+
);
|
|
3727
3802
|
}
|
|
3728
3803
|
const activation = await (0, import_sdk_core8.activateSessionWithHost)(
|
|
3729
3804
|
host,
|
|
3730
3805
|
session.delegationHeader
|
|
3731
3806
|
);
|
|
3732
3807
|
if (!activation.success || activation.skipped?.includes(spaceId)) {
|
|
3733
|
-
throw
|
|
3734
|
-
|
|
3808
|
+
throw Object.assign(
|
|
3809
|
+
new Error(
|
|
3810
|
+
`Failed to activate session for owned space ${spaceId}: ${activation.success ? "space was skipped" : describeHostFailure2(activation)}`
|
|
3811
|
+
),
|
|
3812
|
+
{ cause: activation }
|
|
3735
3813
|
);
|
|
3736
3814
|
}
|
|
3737
3815
|
try {
|
|
@@ -3927,23 +4005,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3927
4005
|
throw new InvalidRestoredSessionError(error instanceof Error ? error.message : String(error));
|
|
3928
4006
|
}
|
|
3929
4007
|
})();
|
|
3930
|
-
|
|
3931
|
-
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
3932
|
-
(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(
|
|
3933
|
-
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
3934
|
-
)
|
|
3935
|
-
)) {
|
|
3936
|
-
throw new UnsupportedSessionRestoreError(
|
|
3937
|
-
"it cannot reconstruct caveat-preserving persisted ReCap authority"
|
|
3938
|
-
);
|
|
3939
|
-
}
|
|
3940
|
-
stagedRecap = exactRecap.map((entry) => ({
|
|
3941
|
-
service: entry.service,
|
|
3942
|
-
space: entry.space,
|
|
3943
|
-
path: entry.path,
|
|
3944
|
-
actions: [...entry.actions],
|
|
3945
|
-
caveats: cloneRecapCaveats(entry.caveats)
|
|
3946
|
-
}));
|
|
4008
|
+
stagedRecap = exactVerifiedRecap(verified);
|
|
3947
4009
|
const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
|
|
3948
4010
|
const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
|
|
3949
4011
|
if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
|
|
@@ -4747,7 +4809,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4747
4809
|
if (cached) return cached;
|
|
4748
4810
|
const response = await fetch(`${this.config.host}/info`);
|
|
4749
4811
|
if (!response.ok) {
|
|
4750
|
-
throw
|
|
4812
|
+
throw await (0, import_sdk_core8.httpResponseError)(response, "Failed to fetch node info");
|
|
4751
4813
|
}
|
|
4752
4814
|
const info = await response.json();
|
|
4753
4815
|
if (typeof info.nodeId !== "string" || info.nodeId.length === 0) {
|
|
@@ -4797,9 +4859,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4797
4859
|
);
|
|
4798
4860
|
if (response.status === 404) return null;
|
|
4799
4861
|
if (!response.ok) {
|
|
4800
|
-
throw
|
|
4801
|
-
`Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
4802
|
-
);
|
|
4862
|
+
throw await (0, import_sdk_core8.httpResponseError)(response, `Failed to fetch encryption network ${networkId}`);
|
|
4803
4863
|
}
|
|
4804
4864
|
const body = await response.json();
|
|
4805
4865
|
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
@@ -5232,7 +5292,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5232
5292
|
const activation = await (0, import_sdk_core8.activateSessionWithHost)(host, delegationSession.delegationHeader);
|
|
5233
5293
|
assertOwnerGraphActive();
|
|
5234
5294
|
if (!activation.success) {
|
|
5235
|
-
throw
|
|
5295
|
+
throw Object.assign(
|
|
5296
|
+
new Error(`Owner delegation import failed: ${activation.status} ${activation.error ?? ""}`.trim()),
|
|
5297
|
+
{ cause: activation }
|
|
5298
|
+
);
|
|
5236
5299
|
}
|
|
5237
5300
|
const delegation = {
|
|
5238
5301
|
cid: delegationSession.delegationCid,
|
|
@@ -5297,7 +5360,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5297
5360
|
headers: { accept: "application/json", "content-type": "application/json", authorization },
|
|
5298
5361
|
body: (0, import_sdk_core8.canonicalizeEncryptionJson)(request)
|
|
5299
5362
|
});
|
|
5300
|
-
if (!response.ok) throw
|
|
5363
|
+
if (!response.ok) throw await (0, import_sdk_core8.httpResponseError)(response, "V3 share delivery authorization failed");
|
|
5301
5364
|
const verified = (0, import_sdk_core8.validateShareDeliveryAuthorizationV3Bytes)(new Uint8Array(await response.arrayBuffer()), {
|
|
5302
5365
|
request,
|
|
5303
5366
|
senderKeyDid: this.credentialHolderDid,
|
|
@@ -5519,9 +5582,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5519
5582
|
);
|
|
5520
5583
|
}
|
|
5521
5584
|
if (!response.ok) {
|
|
5522
|
-
throw
|
|
5523
|
-
`Failed to create encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
5524
|
-
);
|
|
5585
|
+
throw await (0, import_sdk_core8.httpResponseError)(response, `Failed to create encryption network ${networkId}`);
|
|
5525
5586
|
}
|
|
5526
5587
|
const created = await response.json();
|
|
5527
5588
|
return created.descriptor;
|
|
@@ -5805,6 +5866,42 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5805
5866
|
(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"))
|
|
5806
5867
|
);
|
|
5807
5868
|
}
|
|
5869
|
+
/**
|
|
5870
|
+
* Verify a wallet-signed session grant (a CACAO built from a SIWE and its
|
|
5871
|
+
* EIP-191 signature) issued to this instance's live session key. The
|
|
5872
|
+
* verifier rebuilds the CACAO from `siwe` and `signature`, so the grant is
|
|
5873
|
+
* accepted only when its authorization bytes and CID are exactly that CACAO,
|
|
5874
|
+
* its signer is `address`, its audience is this session key, it is valid
|
|
5875
|
+
* now, and its ReCap covers `spaceId` or names only raw encryption networks
|
|
5876
|
+
* (a decrypt-only grant has no space). Returns the signed ReCap and expiry.
|
|
5877
|
+
* Bindings without `validateSessionGrant` fall back to the session verifier,
|
|
5878
|
+
* which also refuses decrypt-only grants.
|
|
5879
|
+
*/
|
|
5880
|
+
verifySessionGrant(grant) {
|
|
5881
|
+
const bindings = this.wasmBindings;
|
|
5882
|
+
const validate = typeof bindings.validateSessionGrant === "function" ? bindings.validateSessionGrant.bind(bindings) : bindings.validatePersistedSession?.bind(bindings);
|
|
5883
|
+
if (validate === void 0) {
|
|
5884
|
+
throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
|
|
5885
|
+
}
|
|
5886
|
+
const verified = validate({
|
|
5887
|
+
delegationHeader: { Authorization: grant.delegationHeader.Authorization },
|
|
5888
|
+
delegationCid: grant.delegationCid,
|
|
5889
|
+
spaceId: grant.spaceId,
|
|
5890
|
+
jwk: this.sessionKeyJwk,
|
|
5891
|
+
address: (0, import_sdk_core8.canonicalizeAddress)(grant.address),
|
|
5892
|
+
chainId: grant.chainId,
|
|
5893
|
+
siwe: grant.siwe,
|
|
5894
|
+
signature: grant.signature
|
|
5895
|
+
});
|
|
5896
|
+
if (typeof verified.expiresAt !== "string") {
|
|
5897
|
+
throw new Error("Session grant has no signed expiry.");
|
|
5898
|
+
}
|
|
5899
|
+
const expiresAt = new Date(verified.expiresAt);
|
|
5900
|
+
if (!Number.isFinite(expiresAt.getTime())) {
|
|
5901
|
+
throw new Error("Session grant has an invalid signed expiry.");
|
|
5902
|
+
}
|
|
5903
|
+
return { recap: exactVerifiedRecap(verified), expiresAt };
|
|
5904
|
+
}
|
|
5808
5905
|
/**
|
|
5809
5906
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
5810
5907
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -5829,8 +5926,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5829
5926
|
delegation.delegationHeader
|
|
5830
5927
|
);
|
|
5831
5928
|
if (!activateResult.success) {
|
|
5832
|
-
throw
|
|
5833
|
-
`Failed to activate runtime permission delegation: ${activateResult
|
|
5929
|
+
throw Object.assign(
|
|
5930
|
+
new Error(`Failed to activate runtime permission delegation: ${describeHostFailure2(activateResult)}`),
|
|
5931
|
+
{ cause: activateResult }
|
|
5834
5932
|
);
|
|
5835
5933
|
}
|
|
5836
5934
|
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
@@ -5926,8 +6024,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5926
6024
|
delegatedSession.delegationHeader
|
|
5927
6025
|
);
|
|
5928
6026
|
if (!activateResult.success) {
|
|
5929
|
-
throw
|
|
5930
|
-
`Failed to activate runtime permission delegation: ${activateResult
|
|
6027
|
+
throw Object.assign(
|
|
6028
|
+
new Error(`Failed to activate runtime permission delegation: ${describeHostFailure2(activateResult)}`),
|
|
6029
|
+
{ cause: activateResult }
|
|
5931
6030
|
);
|
|
5932
6031
|
}
|
|
5933
6032
|
const delegation = this.runtimeDelegationFromSession(
|
|
@@ -5935,7 +6034,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5935
6034
|
delegatedEntries,
|
|
5936
6035
|
spaceId,
|
|
5937
6036
|
session,
|
|
5938
|
-
expiresAt
|
|
6037
|
+
expiresAt,
|
|
6038
|
+
{ siwe: prepared.siwe, signature }
|
|
5939
6039
|
);
|
|
5940
6040
|
this.runtimePermissionGrants.push({
|
|
5941
6041
|
session: {
|
|
@@ -6112,7 +6212,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6112
6212
|
delegationSession.delegationHeader
|
|
6113
6213
|
);
|
|
6114
6214
|
if (!activateResult.success) {
|
|
6115
|
-
throw
|
|
6215
|
+
throw Object.assign(
|
|
6216
|
+
new Error(`Failed to activate public space delegation: ${describeHostFailure2(activateResult)}`),
|
|
6217
|
+
{ cause: activateResult }
|
|
6218
|
+
);
|
|
6116
6219
|
}
|
|
6117
6220
|
if (this._capabilityRegistry && this.session) {
|
|
6118
6221
|
const sessionKey = {
|
|
@@ -6465,8 +6568,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6465
6568
|
delegationHeader
|
|
6466
6569
|
);
|
|
6467
6570
|
if (!activateResult.success) {
|
|
6468
|
-
throw
|
|
6469
|
-
`Failed to activate delegation with host: ${activateResult
|
|
6571
|
+
throw Object.assign(
|
|
6572
|
+
new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
|
|
6573
|
+
{ cause: activateResult }
|
|
6470
6574
|
);
|
|
6471
6575
|
}
|
|
6472
6576
|
return {
|
|
@@ -6502,8 +6606,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6502
6606
|
delegationHeader
|
|
6503
6607
|
);
|
|
6504
6608
|
if (!activateResult.success) {
|
|
6505
|
-
throw
|
|
6506
|
-
`Failed to activate delegation with host: ${activateResult
|
|
6609
|
+
throw Object.assign(
|
|
6610
|
+
new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
|
|
6611
|
+
{ cause: activateResult }
|
|
6507
6612
|
);
|
|
6508
6613
|
}
|
|
6509
6614
|
return {
|
|
@@ -6716,7 +6821,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6716
6821
|
}
|
|
6717
6822
|
return grants;
|
|
6718
6823
|
}
|
|
6719
|
-
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
|
|
6824
|
+
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt, siweProof) {
|
|
6720
6825
|
const resources = this.delegatedResourcesForEntries(entries, spaceId);
|
|
6721
6826
|
const primary = resources[0];
|
|
6722
6827
|
return {
|
|
@@ -6731,7 +6836,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6731
6836
|
delegateDID: session.verificationMethod,
|
|
6732
6837
|
ownerAddress: session.address,
|
|
6733
6838
|
chainId: session.chainId,
|
|
6734
|
-
host: this.config.host
|
|
6839
|
+
host: this.config.host,
|
|
6840
|
+
siweProof
|
|
6735
6841
|
};
|
|
6736
6842
|
}
|
|
6737
6843
|
runtimeGrantFromDelegation(delegation, session) {
|
|
@@ -6924,6 +7030,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6924
7030
|
if (grantedAction === requestedAction) {
|
|
6925
7031
|
return true;
|
|
6926
7032
|
}
|
|
7033
|
+
if ((0, import_sdk_core8.isExplicitOnlyAction)(requestedAction)) {
|
|
7034
|
+
return false;
|
|
7035
|
+
}
|
|
6927
7036
|
if (grantedAction.endsWith("/*")) {
|
|
6928
7037
|
const prefix = grantedAction.slice(0, -2);
|
|
6929
7038
|
return requestedAction.startsWith(`${prefix}/`);
|
|
@@ -7110,7 +7219,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7110
7219
|
delegationSession.delegationHeader
|
|
7111
7220
|
);
|
|
7112
7221
|
if (!activateResult.success) {
|
|
7113
|
-
throw
|
|
7222
|
+
throw Object.assign(
|
|
7223
|
+
new Error(`Failed to activate delegation: ${describeHostFailure2(activateResult)}`),
|
|
7224
|
+
{ cause: activateResult }
|
|
7225
|
+
);
|
|
7114
7226
|
}
|
|
7115
7227
|
const result = {
|
|
7116
7228
|
cid: delegationSession.delegationCid,
|
|
@@ -7260,7 +7372,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7260
7372
|
invokerSession.delegationHeader
|
|
7261
7373
|
);
|
|
7262
7374
|
if (!activateResult.success) {
|
|
7263
|
-
throw
|
|
7375
|
+
throw Object.assign(
|
|
7376
|
+
new Error(`Failed to activate delegated session: ${describeHostFailure2(activateResult)}`),
|
|
7377
|
+
{ cause: activateResult }
|
|
7378
|
+
);
|
|
7264
7379
|
}
|
|
7265
7380
|
const session = {
|
|
7266
7381
|
address: mySession.address,
|
|
@@ -7377,7 +7492,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7377
7492
|
subDelegationSession.delegationHeader
|
|
7378
7493
|
);
|
|
7379
7494
|
if (!activateResult.success) {
|
|
7380
|
-
throw
|
|
7495
|
+
throw Object.assign(
|
|
7496
|
+
new Error(`Failed to activate sub-delegation: ${describeHostFailure2(activateResult)}`),
|
|
7497
|
+
{ cause: activateResult }
|
|
7498
|
+
);
|
|
7381
7499
|
}
|
|
7382
7500
|
return {
|
|
7383
7501
|
cid: subDelegationSession.delegationCid,
|
|
@@ -7418,6 +7536,16 @@ var import_sdk_core12 = require("@tinycloud/sdk-core");
|
|
|
7418
7536
|
|
|
7419
7537
|
// src/delegation.ts
|
|
7420
7538
|
var import_sdk_core9 = require("@tinycloud/sdk-core");
|
|
7539
|
+
var RuntimeDelegationRejectedError = class extends Error {
|
|
7540
|
+
constructor(reason, message) {
|
|
7541
|
+
super(message);
|
|
7542
|
+
this.name = "RuntimeDelegationRejectedError";
|
|
7543
|
+
this.reason = reason;
|
|
7544
|
+
}
|
|
7545
|
+
};
|
|
7546
|
+
function reject(reason, message) {
|
|
7547
|
+
throw new RuntimeDelegationRejectedError(reason, message);
|
|
7548
|
+
}
|
|
7421
7549
|
var SERVICE_BY_SHORT_NAME = {
|
|
7422
7550
|
kv: "tinycloud.kv",
|
|
7423
7551
|
sql: "tinycloud.sql",
|
|
@@ -7564,6 +7692,15 @@ function signedAuthorityFromCompactUcan(authorization) {
|
|
|
7564
7692
|
abilities
|
|
7565
7693
|
));
|
|
7566
7694
|
}
|
|
7695
|
+
return canonicalSignedAuthority({
|
|
7696
|
+
issuer: payload.iss,
|
|
7697
|
+
audience: payload.aud,
|
|
7698
|
+
expiry,
|
|
7699
|
+
proofs: [...payload.prf],
|
|
7700
|
+
...remainingRedelegationDepth === void 0 ? {} : { remainingRedelegationDepth }
|
|
7701
|
+
}, permissions);
|
|
7702
|
+
}
|
|
7703
|
+
function canonicalSignedAuthority(signed, permissions) {
|
|
7567
7704
|
if (permissions.length === 0) {
|
|
7568
7705
|
throw new Error("Validated runtime delegation has no signed capabilities.");
|
|
7569
7706
|
}
|
|
@@ -7575,15 +7712,84 @@ function signedAuthorityFromCompactUcan(authorization) {
|
|
|
7575
7712
|
actions: [...permission.actions],
|
|
7576
7713
|
...permission.caveats === void 0 ? {} : { caveats: permission.caveats }
|
|
7577
7714
|
}));
|
|
7578
|
-
return {
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7715
|
+
return { ...signed, permissions: canonicalPermissions, resources: canonicalResources };
|
|
7716
|
+
}
|
|
7717
|
+
var RAW_ENCRYPTION_PREFIX = "urn:tinycloud:encryption:";
|
|
7718
|
+
var PKH_SPACE = /^tinycloud:pkh:eip155:(\d+):(0x[0-9a-fA-F]{40}):(.+)$/;
|
|
7719
|
+
function comparableSpace(space) {
|
|
7720
|
+
const match = PKH_SPACE.exec(space);
|
|
7721
|
+
return match ? `tinycloud:pkh:eip155:${match[1]}:${match[2].toLowerCase()}:${match[3]}` : space;
|
|
7722
|
+
}
|
|
7723
|
+
function signedAuthorityFromSessionGrant(node, delegation, authorization, cid) {
|
|
7724
|
+
const proof = delegation.siweProof;
|
|
7725
|
+
if (proof === void 0 || proof === null || typeof proof !== "object" || typeof proof.siwe !== "string" || typeof proof.signature !== "string") {
|
|
7726
|
+
reject(
|
|
7727
|
+
"proof_missing",
|
|
7728
|
+
"Runtime delegation is a wallet-signed session grant stored without its signed SIWE proof, so its authority cannot be verified."
|
|
7729
|
+
);
|
|
7730
|
+
}
|
|
7731
|
+
if (typeof node.verifySessionGrant !== "function") {
|
|
7732
|
+
reject("unsupported", "This runtime cannot verify wallet-signed session grants.");
|
|
7733
|
+
}
|
|
7734
|
+
const owner = `did:pkh:eip155:${delegation.chainId}:${delegation.ownerAddress}`;
|
|
7735
|
+
if (typeof node.did !== "string" || !didPrincipalsMatch(owner, node.did)) {
|
|
7736
|
+
reject("owner_mismatch", "Runtime delegation is not signed by this session's owner.");
|
|
7737
|
+
}
|
|
7738
|
+
let verified;
|
|
7739
|
+
try {
|
|
7740
|
+
verified = node.verifySessionGrant({
|
|
7741
|
+
delegationHeader: { Authorization: authorization },
|
|
7742
|
+
delegationCid: cid,
|
|
7743
|
+
spaceId: delegation.spaceId,
|
|
7744
|
+
address: delegation.ownerAddress,
|
|
7745
|
+
chainId: delegation.chainId,
|
|
7746
|
+
siwe: proof.siwe,
|
|
7747
|
+
signature: proof.signature
|
|
7748
|
+
});
|
|
7749
|
+
} catch (error) {
|
|
7750
|
+
reject("proof_invalid", error instanceof Error ? error.message : String(error));
|
|
7751
|
+
}
|
|
7752
|
+
const space = comparableSpace(delegation.spaceId);
|
|
7753
|
+
const spaceOwner = PKH_SPACE.exec(delegation.spaceId);
|
|
7754
|
+
if (!spaceOwner || !didPrincipalsMatch(`did:pkh:eip155:${spaceOwner[1]}:${spaceOwner[2]}`, owner)) {
|
|
7755
|
+
reject("authority_invalid", "Runtime delegation space is not owned by its signer.");
|
|
7756
|
+
}
|
|
7757
|
+
const groups = /* @__PURE__ */ new Map();
|
|
7758
|
+
for (const entry of verified.recap) {
|
|
7759
|
+
const service = entry.service.startsWith("tinycloud.") ? entry.service : `tinycloud.${entry.service}`;
|
|
7760
|
+
const raw = entry.space === "encryption";
|
|
7761
|
+
if (raw) {
|
|
7762
|
+
if (service !== "tinycloud.encryption" || !entry.path.startsWith(RAW_ENCRYPTION_PREFIX)) {
|
|
7763
|
+
reject("authority_invalid", "Runtime delegation has an unsupported signed raw resource.");
|
|
7764
|
+
}
|
|
7765
|
+
const networkOwner = entry.path.slice(RAW_ENCRYPTION_PREFIX.length, entry.path.lastIndexOf(":"));
|
|
7766
|
+
if (!didPrincipalsMatch(networkOwner, owner)) {
|
|
7767
|
+
reject("authority_invalid", "Runtime delegation names an encryption network its signer does not own.");
|
|
7768
|
+
}
|
|
7769
|
+
} else if (service === "tinycloud.encryption" || comparableSpace(entry.space) !== space) {
|
|
7770
|
+
reject("authority_invalid", "Runtime delegation has signed authority outside its space.");
|
|
7771
|
+
}
|
|
7772
|
+
const caveats = signedCaveats(entry.caveats ?? []);
|
|
7773
|
+
const key = JSON.stringify([service, raw ? "" : entry.space, entry.path, (0, import_sdk_core9.canonicalizeRecapCaveats)(caveats)]);
|
|
7774
|
+
const group = groups.get(key) ?? {
|
|
7775
|
+
service,
|
|
7776
|
+
...raw ? {} : { space: entry.space },
|
|
7777
|
+
path: entry.path,
|
|
7778
|
+
actions: [],
|
|
7779
|
+
...caveats === void 0 ? {} : { caveats }
|
|
7780
|
+
};
|
|
7781
|
+
for (const action of entry.actions) {
|
|
7782
|
+
if (!group.actions.includes(action)) group.actions.push(action);
|
|
7783
|
+
}
|
|
7784
|
+
groups.set(key, group);
|
|
7785
|
+
}
|
|
7786
|
+
return canonicalSignedAuthority({
|
|
7787
|
+
issuer: owner,
|
|
7788
|
+
// The verifier bound the SIWE audience to this runtime's own session key.
|
|
7789
|
+
audience: node.sessionDid,
|
|
7790
|
+
expiry: verified.expiresAt,
|
|
7791
|
+
proofs: []
|
|
7792
|
+
}, [...groups.values()]);
|
|
7587
7793
|
}
|
|
7588
7794
|
function canonicalResourcesFromPortableDelegation(delegation) {
|
|
7589
7795
|
if (delegation.resources !== void 0 && delegation.resources.length > 0) {
|
|
@@ -7619,46 +7825,58 @@ function resourcesMatch(actual, expected) {
|
|
|
7619
7825
|
async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
7620
7826
|
const host = normalizedHost(options.host);
|
|
7621
7827
|
if (delegation.host !== void 0 && normalizedHost(delegation.host) !== host) {
|
|
7622
|
-
|
|
7828
|
+
reject(
|
|
7829
|
+
"host_mismatch",
|
|
7623
7830
|
`Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`
|
|
7624
7831
|
);
|
|
7625
7832
|
}
|
|
7626
7833
|
if (!(delegation.expiry instanceof Date) || Number.isNaN(delegation.expiry.getTime())) {
|
|
7627
|
-
|
|
7834
|
+
reject("authority_invalid", "Runtime delegation has an invalid expiry.");
|
|
7628
7835
|
}
|
|
7629
7836
|
if (delegation.expiry.getTime() <= Date.now()) {
|
|
7630
|
-
|
|
7837
|
+
reject("expired", "Runtime delegation is expired.");
|
|
7631
7838
|
}
|
|
7632
7839
|
const authorization = delegation.delegationHeader?.Authorization;
|
|
7633
7840
|
if (typeof authorization !== "string" || authorization.length === 0) {
|
|
7634
|
-
|
|
7841
|
+
reject("authority_invalid", "Runtime delegation is missing authorization bytes.");
|
|
7635
7842
|
}
|
|
7636
7843
|
const cid = node.computeDelegationCid(authorization);
|
|
7637
7844
|
if (cid !== delegation.cid) {
|
|
7638
|
-
|
|
7845
|
+
reject("cid_mismatch", "Runtime delegation CID does not match authorization bytes.");
|
|
7846
|
+
}
|
|
7847
|
+
const compact = authorizationWithoutBearer(authorization).split(".").length === 3;
|
|
7848
|
+
let signed;
|
|
7849
|
+
try {
|
|
7850
|
+
signed = compact ? signedAuthorityFromCompactUcan(authorization) : signedAuthorityFromSessionGrant(node, delegation, authorization, cid);
|
|
7851
|
+
} catch (error) {
|
|
7852
|
+
if (error instanceof RuntimeDelegationRejectedError) throw error;
|
|
7853
|
+
reject("authority_invalid", error instanceof Error ? error.message : String(error));
|
|
7639
7854
|
}
|
|
7640
|
-
const signed = signedAuthorityFromCompactUcan(authorization);
|
|
7641
7855
|
if (signed.expiry.getTime() <= Date.now()) {
|
|
7642
|
-
|
|
7856
|
+
reject("expired", "Runtime delegation is expired.");
|
|
7643
7857
|
}
|
|
7644
7858
|
if (delegation.expiry.getTime() !== signed.expiry.getTime()) {
|
|
7645
|
-
|
|
7859
|
+
reject("authority_invalid", "Runtime delegation expiry does not match signed authority.");
|
|
7646
7860
|
}
|
|
7647
7861
|
if (!didPrincipalsMatch(signed.audience, node.sessionDid)) {
|
|
7648
|
-
|
|
7862
|
+
reject(
|
|
7863
|
+
"audience_mismatch",
|
|
7649
7864
|
`Runtime delegation targets ${signed.audience} but this session is ${node.sessionDid}.`
|
|
7650
7865
|
);
|
|
7651
7866
|
}
|
|
7652
7867
|
if (!didPrincipalsMatch(delegation.delegateDID, signed.audience)) {
|
|
7653
|
-
|
|
7868
|
+
reject("audience_mismatch", "Runtime delegation audience does not match signed authority.");
|
|
7869
|
+
}
|
|
7870
|
+
if (compact && !resourcesMatch(canonicalResourcesFromPortableDelegation(delegation), signed.resources)) {
|
|
7871
|
+
reject("resources_mismatch", "Runtime delegation resources do not match signed authority.");
|
|
7654
7872
|
}
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
throw new Error("Runtime delegation resources do not match signed authority.");
|
|
7873
|
+
if (options.authorize !== void 0 && !options.authorize(signed.permissions)) {
|
|
7874
|
+
throw new Error("Runtime delegation is not authorized for this runtime.");
|
|
7658
7875
|
}
|
|
7659
7876
|
const previouslyInstalled = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
7660
7877
|
if (previouslyInstalled !== void 0 && previouslyInstalled.delegationHeader.Authorization !== authorization) {
|
|
7661
|
-
|
|
7878
|
+
reject(
|
|
7879
|
+
"cid_mismatch",
|
|
7662
7880
|
"A different authorization is already installed for this runtime delegation CID."
|
|
7663
7881
|
);
|
|
7664
7882
|
}
|
|
@@ -7688,22 +7906,28 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
|
7688
7906
|
})),
|
|
7689
7907
|
expiry: new Date(signed.expiry),
|
|
7690
7908
|
delegateDID: signed.audience,
|
|
7691
|
-
host
|
|
7909
|
+
host,
|
|
7910
|
+
...compact ? {} : { siweProof: delegation.siweProof }
|
|
7692
7911
|
};
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7912
|
+
try {
|
|
7913
|
+
if (node.isSessionOnly === true && typeof node.did === "string" && (0, import_sdk_core9.principalDidEquals)(node.did, node.sessionDid) && typeof node.useDelegation === "function") {
|
|
7914
|
+
await node.useDelegation(installedCandidate);
|
|
7915
|
+
} else {
|
|
7916
|
+
await node.useRuntimeDelegation(installedCandidate);
|
|
7917
|
+
}
|
|
7918
|
+
} catch (error) {
|
|
7919
|
+
reject("activation_rejected", error instanceof Error ? error.message : String(error));
|
|
7697
7920
|
}
|
|
7698
7921
|
const installed = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
7699
7922
|
if (!installed) {
|
|
7700
|
-
|
|
7923
|
+
reject("activation_rejected", "Runtime delegation activation did not install the validated authority.");
|
|
7701
7924
|
}
|
|
7702
7925
|
if (installed.delegationHeader.Authorization !== authorization || !didPrincipalsMatch(installed.delegateDID, signed.audience) || installed.expiry.getTime() !== signed.expiry.getTime() || !resourcesMatch(
|
|
7703
7926
|
canonicalResourcesFromPortableDelegation(installed),
|
|
7704
7927
|
signed.resources
|
|
7705
7928
|
) || normalizedHost(installed.host ?? "") !== host) {
|
|
7706
|
-
|
|
7929
|
+
reject(
|
|
7930
|
+
"activation_rejected",
|
|
7707
7931
|
"Runtime delegation activation installed authority that differs from the validated authority."
|
|
7708
7932
|
);
|
|
7709
7933
|
}
|