@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.js
CHANGED
|
@@ -262,6 +262,10 @@ var defaultSignStrategy = { type: "auto-sign" };
|
|
|
262
262
|
// src/authorization/NodeUserAuthorization.ts
|
|
263
263
|
var DECRYPT_ACTION = ENCRYPTION.DECRYPT;
|
|
264
264
|
var NETWORK_CREATE_ACTION = ENCRYPTION.NETWORK_CREATE;
|
|
265
|
+
function describeHostFailure(result) {
|
|
266
|
+
const text = result.error?.trim();
|
|
267
|
+
return text ? `${result.status} - ${text}` : `${result.status}`;
|
|
268
|
+
}
|
|
265
269
|
function canonicalizeOpenKeyManifestJson(value) {
|
|
266
270
|
const json = JSON.stringify(value);
|
|
267
271
|
if (json === void 0) {
|
|
@@ -726,21 +730,28 @@ var NodeUserAuthorization = class {
|
|
|
726
730
|
});
|
|
727
731
|
const signature = await this.signMessage(siwe, purpose);
|
|
728
732
|
const headers = this.wasm.siweToDelegationHeaders({ siwe, signature });
|
|
729
|
-
|
|
730
|
-
return result.success;
|
|
733
|
+
return submitHostDelegation(host, headers);
|
|
731
734
|
}
|
|
732
735
|
/**
|
|
733
736
|
* Create a specific space on the server via host delegation.
|
|
734
737
|
* Used for lazy creation of additional spaces (e.g., public).
|
|
735
738
|
*/
|
|
736
739
|
async hostPublicSpace(spaceId) {
|
|
737
|
-
return this.hostSpace(spaceId);
|
|
740
|
+
return (await this.hostSpace(spaceId)).success;
|
|
738
741
|
}
|
|
739
742
|
/**
|
|
740
743
|
* Create a specific owned space on the server via host delegation.
|
|
741
744
|
* Used by manifest registry setup for the account space.
|
|
742
745
|
*/
|
|
743
746
|
async hostOwnedSpace(spaceId, purpose) {
|
|
747
|
+
return (await this.hostOwnedSpaceResult(spaceId, purpose)).success;
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* {@link hostOwnedSpace} with the full host-delegation result, so a failure
|
|
751
|
+
* keeps its HTTP status (e.g. a typed 401/403) instead of collapsing to
|
|
752
|
+
* `false`.
|
|
753
|
+
*/
|
|
754
|
+
async hostOwnedSpaceResult(spaceId, purpose) {
|
|
744
755
|
return this.hostSpace(spaceId, purpose);
|
|
745
756
|
}
|
|
746
757
|
/**
|
|
@@ -784,9 +795,11 @@ var NodeUserAuthorization = class {
|
|
|
784
795
|
}
|
|
785
796
|
try {
|
|
786
797
|
const created = await this.hostSpace();
|
|
787
|
-
if (!created) {
|
|
788
|
-
const err =
|
|
789
|
-
|
|
798
|
+
if (!created.success) {
|
|
799
|
+
const err = Object.assign(
|
|
800
|
+
new Error(`Failed to create space ${primarySpaceId}: ${describeHostFailure(created)}`),
|
|
801
|
+
{ cause: created }
|
|
802
|
+
);
|
|
790
803
|
throw err;
|
|
791
804
|
}
|
|
792
805
|
} catch (error) {
|
|
@@ -802,8 +815,9 @@ var NodeUserAuthorization = class {
|
|
|
802
815
|
this._tinyCloudSession.delegationHeader
|
|
803
816
|
);
|
|
804
817
|
if (!retryResult.success) {
|
|
805
|
-
const err =
|
|
806
|
-
`Failed to activate session after creating space: ${retryResult
|
|
818
|
+
const err = Object.assign(
|
|
819
|
+
new Error(`Failed to activate session after creating space: ${describeHostFailure(retryResult)}`),
|
|
820
|
+
{ cause: retryResult }
|
|
807
821
|
);
|
|
808
822
|
handler.onSpaceCreationFailed?.(creationContext, err);
|
|
809
823
|
throw err;
|
|
@@ -821,9 +835,11 @@ var NodeUserAuthorization = class {
|
|
|
821
835
|
}
|
|
822
836
|
try {
|
|
823
837
|
const created = await this.hostSpace();
|
|
824
|
-
if (!created) {
|
|
825
|
-
const err =
|
|
826
|
-
|
|
838
|
+
if (!created.success) {
|
|
839
|
+
const err = Object.assign(
|
|
840
|
+
new Error(`Failed to create space ${primarySpaceId}: ${describeHostFailure(created)}`),
|
|
841
|
+
{ cause: created }
|
|
842
|
+
);
|
|
827
843
|
throw err;
|
|
828
844
|
}
|
|
829
845
|
} catch (error) {
|
|
@@ -839,8 +855,9 @@ var NodeUserAuthorization = class {
|
|
|
839
855
|
this._tinyCloudSession.delegationHeader
|
|
840
856
|
);
|
|
841
857
|
if (!retryResult.success) {
|
|
842
|
-
const err =
|
|
843
|
-
`Failed to activate session after creating space: ${retryResult
|
|
858
|
+
const err = Object.assign(
|
|
859
|
+
new Error(`Failed to activate session after creating space: ${describeHostFailure(retryResult)}`),
|
|
860
|
+
{ cause: retryResult }
|
|
844
861
|
);
|
|
845
862
|
handler.onSpaceCreationFailed?.(creationContext, err);
|
|
846
863
|
throw err;
|
|
@@ -848,7 +865,10 @@ var NodeUserAuthorization = class {
|
|
|
848
865
|
handler.onSpaceCreated?.(creationContext);
|
|
849
866
|
return;
|
|
850
867
|
}
|
|
851
|
-
throw
|
|
868
|
+
throw Object.assign(
|
|
869
|
+
new Error(`Failed to activate session: ${describeHostFailure(result)}`),
|
|
870
|
+
{ cause: result }
|
|
871
|
+
);
|
|
852
872
|
}
|
|
853
873
|
recordActivationSkippedSpaces(result, primarySpaceId) {
|
|
854
874
|
if (result.success) {
|
|
@@ -1638,14 +1658,14 @@ var NodeUserAuthorization = class {
|
|
|
1638
1658
|
* Request signature via event emitter with timeout.
|
|
1639
1659
|
*/
|
|
1640
1660
|
requestSignatureViaEmitter(request, emitter, timeout) {
|
|
1641
|
-
return new Promise((resolve,
|
|
1661
|
+
return new Promise((resolve, reject2) => {
|
|
1642
1662
|
const timeoutId = setTimeout(() => {
|
|
1643
|
-
|
|
1663
|
+
reject2(new Error("Sign request timed out"));
|
|
1644
1664
|
}, timeout);
|
|
1645
1665
|
const respond = async (response) => {
|
|
1646
1666
|
clearTimeout(timeoutId);
|
|
1647
1667
|
if (!response.approved) {
|
|
1648
|
-
|
|
1668
|
+
reject2(
|
|
1649
1669
|
new Error(response.reason ?? "Sign request rejected via emitter")
|
|
1650
1670
|
);
|
|
1651
1671
|
} else {
|
|
@@ -1662,6 +1682,8 @@ var NodeUserAuthorization = class {
|
|
|
1662
1682
|
import {
|
|
1663
1683
|
TinyCloud,
|
|
1664
1684
|
activateSessionWithHost as activateSessionWithHost2,
|
|
1685
|
+
authorizationVerdictOf,
|
|
1686
|
+
httpResponseError,
|
|
1665
1687
|
KVService as KVService2,
|
|
1666
1688
|
SQLService as SQLService2,
|
|
1667
1689
|
DuckDbService as DuckDbService2,
|
|
@@ -1695,6 +1717,7 @@ import {
|
|
|
1695
1717
|
recapCaveatsEqual,
|
|
1696
1718
|
expandPermissionEntries as expandPermissionEntriesCore,
|
|
1697
1719
|
isCapabilitySubset,
|
|
1720
|
+
isExplicitOnlyAction,
|
|
1698
1721
|
parseRecapCapabilities,
|
|
1699
1722
|
SERVICE_LONG_TO_SHORT,
|
|
1700
1723
|
SERVICE_SHORT_TO_LONG,
|
|
@@ -1717,7 +1740,10 @@ import {
|
|
|
1717
1740
|
CREDENTIAL_INVITATION_REQUEST_DOMAIN,
|
|
1718
1741
|
validateShareDeliveryAuthorizationV3Bytes,
|
|
1719
1742
|
verifyEip191MessageSignature,
|
|
1720
|
-
signCompactUcanRootAuthorization
|
|
1743
|
+
signCompactUcanRootAuthorization,
|
|
1744
|
+
isStorageFullError,
|
|
1745
|
+
STORAGE_FULL_MESSAGE,
|
|
1746
|
+
STORAGE_WRITE_TOO_LARGE_MESSAGE
|
|
1721
1747
|
} from "@tinycloud/sdk-core";
|
|
1722
1748
|
import {
|
|
1723
1749
|
parsePermissionHint
|
|
@@ -2305,6 +2331,25 @@ function jsonFromVerifier(value) {
|
|
|
2305
2331
|
function verifierRecapEntries(entries) {
|
|
2306
2332
|
return entries.map((entry) => Array.isArray(entry.caveats) ? { ...entry, caveats: entry.caveats.map((caveat) => jsonFromVerifier(caveat)) } : entry);
|
|
2307
2333
|
}
|
|
2334
|
+
function exactVerifiedRecap(verified) {
|
|
2335
|
+
const exactRecap = Array.isArray(verified.verifiedRecap) ? verifierRecapEntries(verified.verifiedRecap) : verified.verifiedRecap;
|
|
2336
|
+
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
2337
|
+
(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(
|
|
2338
|
+
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
2339
|
+
)
|
|
2340
|
+
)) {
|
|
2341
|
+
throw new UnsupportedSessionRestoreError(
|
|
2342
|
+
"it cannot reconstruct caveat-preserving persisted ReCap authority"
|
|
2343
|
+
);
|
|
2344
|
+
}
|
|
2345
|
+
return exactRecap.map((entry) => ({
|
|
2346
|
+
service: entry.service,
|
|
2347
|
+
space: entry.space,
|
|
2348
|
+
path: entry.path,
|
|
2349
|
+
actions: [...entry.actions],
|
|
2350
|
+
caveats: cloneRecapCaveats(entry.caveats)
|
|
2351
|
+
}));
|
|
2352
|
+
}
|
|
2308
2353
|
var ServiceGraphLifetime = class {
|
|
2309
2354
|
constructor(invokeFn, invokeAnyFn, fetchFn) {
|
|
2310
2355
|
this.invokeFn = invokeFn;
|
|
@@ -2400,6 +2445,10 @@ function persistedExpiry(value) {
|
|
|
2400
2445
|
function sameInstant(left, right) {
|
|
2401
2446
|
return left.getTime() === right.getTime();
|
|
2402
2447
|
}
|
|
2448
|
+
function describeHostFailure2(result) {
|
|
2449
|
+
const text = result.error?.trim();
|
|
2450
|
+
return text ? `${result.status} - ${text}` : `${result.status}`;
|
|
2451
|
+
}
|
|
2403
2452
|
function ownerDelegationPermissions(params) {
|
|
2404
2453
|
const hasPermissions = Object.prototype.hasOwnProperty.call(params, "permissions");
|
|
2405
2454
|
const hasLegacy = Object.prototype.hasOwnProperty.call(params, "path") || Object.prototype.hasOwnProperty.call(params, "actions");
|
|
@@ -3203,7 +3252,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3203
3252
|
marker
|
|
3204
3253
|
);
|
|
3205
3254
|
if (!written.ok) {
|
|
3206
|
-
throw
|
|
3255
|
+
throw Object.assign(
|
|
3256
|
+
new Error(`Failed to write bootstrap completion marker: ${written.error.message}`),
|
|
3257
|
+
{ cause: written.error }
|
|
3258
|
+
);
|
|
3207
3259
|
}
|
|
3208
3260
|
}
|
|
3209
3261
|
isAcceptedBootstrapCompletionMarker(value) {
|
|
@@ -3249,17 +3301,23 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3249
3301
|
rawAbilities
|
|
3250
3302
|
});
|
|
3251
3303
|
} catch (err) {
|
|
3252
|
-
throw
|
|
3253
|
-
|
|
3304
|
+
throw Object.assign(
|
|
3305
|
+
new Error(
|
|
3306
|
+
`Account bootstrap aborted: signature rejected for space "${step.space}". Cause: ${err instanceof Error ? err.message : String(err)}`
|
|
3307
|
+
),
|
|
3308
|
+
{ cause: err }
|
|
3254
3309
|
);
|
|
3255
3310
|
}
|
|
3256
3311
|
sessions.set(step.space, session);
|
|
3257
3312
|
}
|
|
3258
3313
|
for (const step of steps) {
|
|
3259
3314
|
if (step.kind !== "host") continue;
|
|
3260
|
-
const hosted = await auth.
|
|
3261
|
-
if (!hosted) {
|
|
3262
|
-
throw
|
|
3315
|
+
const hosted = await auth.hostOwnedSpaceResult(step.spaceId, "bootstrap-host");
|
|
3316
|
+
if (!hosted.success) {
|
|
3317
|
+
throw Object.assign(
|
|
3318
|
+
new Error(`Failed to host bootstrap space ${step.spaceId}: ${describeHostFailure2(hosted)}`),
|
|
3319
|
+
{ cause: hosted }
|
|
3320
|
+
);
|
|
3263
3321
|
}
|
|
3264
3322
|
}
|
|
3265
3323
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
@@ -3271,8 +3329,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3271
3329
|
}
|
|
3272
3330
|
const activated = await activateSessionWithHost2(host, session.delegationHeader);
|
|
3273
3331
|
if (!activated.success || activated.skipped?.includes(step.spaceId)) {
|
|
3274
|
-
throw
|
|
3275
|
-
|
|
3332
|
+
throw Object.assign(
|
|
3333
|
+
new Error(
|
|
3334
|
+
`Failed to activate bootstrap session for ${step.spaceId}: ${activated.success ? "space was skipped" : describeHostFailure2(activated)}`
|
|
3335
|
+
),
|
|
3336
|
+
{ cause: activated }
|
|
3276
3337
|
);
|
|
3277
3338
|
}
|
|
3278
3339
|
this.registerBootstrapRuntimeGrant(
|
|
@@ -3285,7 +3346,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3285
3346
|
if (step.kind === "account-index-schema") {
|
|
3286
3347
|
const ensured = await this.account.index.ensure();
|
|
3287
3348
|
if (!ensured.ok) {
|
|
3288
|
-
throw new Error(`Failed to create account index schema: ${ensured.error.message}`);
|
|
3349
|
+
throw Object.assign(new Error(`Failed to create account index schema: ${ensured.error.message}`), { cause: ensured.error });
|
|
3289
3350
|
}
|
|
3290
3351
|
}
|
|
3291
3352
|
if (step.kind === "seed-spaces") {
|
|
@@ -3300,8 +3361,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3300
3361
|
}))
|
|
3301
3362
|
);
|
|
3302
3363
|
if (!registered.ok) {
|
|
3303
|
-
throw
|
|
3304
|
-
`Failed to seed account spaces: ${registered.error.message}`
|
|
3364
|
+
throw Object.assign(
|
|
3365
|
+
new Error(`Failed to seed account spaces: ${registered.error.message}`),
|
|
3366
|
+
{ cause: registered.error }
|
|
3305
3367
|
);
|
|
3306
3368
|
}
|
|
3307
3369
|
const batchError = registered.data.recoveredFromBatchError;
|
|
@@ -3322,7 +3384,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3322
3384
|
{ assumeUnregistered: true }
|
|
3323
3385
|
);
|
|
3324
3386
|
if (!registered.ok) {
|
|
3325
|
-
throw new Error(`Failed to seed bootstrap applications: ${registered.error.message}`);
|
|
3387
|
+
throw Object.assign(new Error(`Failed to seed bootstrap applications: ${registered.error.message}`), { cause: registered.error });
|
|
3326
3388
|
}
|
|
3327
3389
|
}
|
|
3328
3390
|
if (step.kind === "encryption-network-create") {
|
|
@@ -3340,8 +3402,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3340
3402
|
]
|
|
3341
3403
|
});
|
|
3342
3404
|
if (!migrated.ok) {
|
|
3343
|
-
throw
|
|
3344
|
-
`Failed to create secret_records schema: ${migrated.error.message}`
|
|
3405
|
+
throw Object.assign(
|
|
3406
|
+
new Error(`Failed to create secret_records schema: ${migrated.error.message}`),
|
|
3407
|
+
{ cause: migrated.error }
|
|
3345
3408
|
);
|
|
3346
3409
|
}
|
|
3347
3410
|
}
|
|
@@ -3513,8 +3576,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3513
3576
|
await this.ensureOwnedSpaceHostedById(accountSpaceId);
|
|
3514
3577
|
const result = await this.account.applications.register(request.manifests);
|
|
3515
3578
|
if (!result.ok) {
|
|
3516
|
-
throw
|
|
3517
|
-
`Failed to write manifest registry records: ${result.error.message}`
|
|
3579
|
+
throw Object.assign(
|
|
3580
|
+
new Error(`Failed to write manifest registry records: ${result.error.message}`),
|
|
3581
|
+
{ cause: result.error }
|
|
3518
3582
|
);
|
|
3519
3583
|
}
|
|
3520
3584
|
}
|
|
@@ -3539,7 +3603,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3539
3603
|
if (this.currentSessionCanListSpaces()) {
|
|
3540
3604
|
const spaces = await this.account.spaces.syncAccessible();
|
|
3541
3605
|
if (!spaces.ok) {
|
|
3542
|
-
throw
|
|
3606
|
+
throw Object.assign(
|
|
3607
|
+
new Error(`Failed to sync account spaces: ${spaces.error.message}`),
|
|
3608
|
+
{ cause: spaces.error }
|
|
3609
|
+
);
|
|
3543
3610
|
}
|
|
3544
3611
|
}
|
|
3545
3612
|
});
|
|
@@ -3594,14 +3661,20 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3594
3661
|
await task();
|
|
3595
3662
|
return;
|
|
3596
3663
|
} catch (error) {
|
|
3664
|
+
const verdict = authorizationVerdictOf(error);
|
|
3597
3665
|
const message = error instanceof Error ? error.message : String(error);
|
|
3598
|
-
|
|
3666
|
+
const isAuthorizationVerdict = verdict === void 0 ? /Unauthorized Action|\b(?:401|403)\b/.test(message) : verdict !== "other";
|
|
3667
|
+
if (isAuthorizationVerdict) {
|
|
3599
3668
|
console.warn(
|
|
3600
3669
|
"TinyCloud account registry sync stopped: authorization verdict is not retryable",
|
|
3601
3670
|
error
|
|
3602
3671
|
);
|
|
3603
3672
|
return;
|
|
3604
3673
|
}
|
|
3674
|
+
if (isStorageFullError(error) || isStorageFullError(error?.cause) || message.includes(STORAGE_FULL_MESSAGE) || message.includes(STORAGE_WRITE_TOO_LARGE_MESSAGE)) {
|
|
3675
|
+
console.warn("TinyCloud account registry sync stopped: storage is full", error);
|
|
3676
|
+
return;
|
|
3677
|
+
}
|
|
3605
3678
|
lastError = error;
|
|
3606
3679
|
if (attempt < delays.length - 1) {
|
|
3607
3680
|
await new Promise((resolve) => setTimeout(resolve, delays[attempt]));
|
|
@@ -3675,19 +3748,26 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3675
3748
|
return;
|
|
3676
3749
|
}
|
|
3677
3750
|
if (!activation.success && activation.status !== 404) {
|
|
3678
|
-
throw
|
|
3679
|
-
`Failed to check owned space ${spaceId}: ${activation
|
|
3751
|
+
throw Object.assign(
|
|
3752
|
+
new Error(`Failed to check owned space ${spaceId}: ${describeHostFailure2(activation)}`),
|
|
3753
|
+
{ cause: activation }
|
|
3680
3754
|
);
|
|
3681
3755
|
}
|
|
3682
|
-
const created = await this.auth.
|
|
3683
|
-
if (!created) {
|
|
3684
|
-
throw
|
|
3756
|
+
const created = await this.auth.hostOwnedSpaceResult(spaceId);
|
|
3757
|
+
if (!created.success) {
|
|
3758
|
+
throw Object.assign(
|
|
3759
|
+
new Error(`Failed to create owned space ${spaceId}: ${describeHostFailure2(created)}`),
|
|
3760
|
+
{ cause: created }
|
|
3761
|
+
);
|
|
3685
3762
|
}
|
|
3686
3763
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
3687
3764
|
const retry = await activateSessionWithHost2(host, session.delegationHeader);
|
|
3688
3765
|
if (!retry.success || retry.skipped?.includes(spaceId)) {
|
|
3689
|
-
throw
|
|
3690
|
-
|
|
3766
|
+
throw Object.assign(
|
|
3767
|
+
new Error(
|
|
3768
|
+
`Failed to activate session after creating owned space ${spaceId}: ${retry.success ? "space was skipped" : describeHostFailure2(retry)}`
|
|
3769
|
+
),
|
|
3770
|
+
{ cause: retry }
|
|
3691
3771
|
);
|
|
3692
3772
|
}
|
|
3693
3773
|
this.confirmedHostedSpaceIds.add(spaceId);
|
|
@@ -3722,19 +3802,23 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3722
3802
|
if (!host) {
|
|
3723
3803
|
throw new Error("Owned space hosting requires a TinyCloud host");
|
|
3724
3804
|
}
|
|
3725
|
-
const hosted = await this.auth.
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3805
|
+
const hosted = await this.auth.hostOwnedSpaceResult(spaceId);
|
|
3806
|
+
if (!hosted.success) {
|
|
3807
|
+
throw Object.assign(
|
|
3808
|
+
new Error(`Failed to host owned space ${spaceId}: ${describeHostFailure2(hosted)}`),
|
|
3809
|
+
{ cause: hosted }
|
|
3810
|
+
);
|
|
3730
3811
|
}
|
|
3731
3812
|
const activation = await activateSessionWithHost2(
|
|
3732
3813
|
host,
|
|
3733
3814
|
session.delegationHeader
|
|
3734
3815
|
);
|
|
3735
3816
|
if (!activation.success || activation.skipped?.includes(spaceId)) {
|
|
3736
|
-
throw
|
|
3737
|
-
|
|
3817
|
+
throw Object.assign(
|
|
3818
|
+
new Error(
|
|
3819
|
+
`Failed to activate session for owned space ${spaceId}: ${activation.success ? "space was skipped" : describeHostFailure2(activation)}`
|
|
3820
|
+
),
|
|
3821
|
+
{ cause: activation }
|
|
3738
3822
|
);
|
|
3739
3823
|
}
|
|
3740
3824
|
try {
|
|
@@ -3930,23 +4014,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3930
4014
|
throw new InvalidRestoredSessionError(error instanceof Error ? error.message : String(error));
|
|
3931
4015
|
}
|
|
3932
4016
|
})();
|
|
3933
|
-
|
|
3934
|
-
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
3935
|
-
(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(
|
|
3936
|
-
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
3937
|
-
)
|
|
3938
|
-
)) {
|
|
3939
|
-
throw new UnsupportedSessionRestoreError(
|
|
3940
|
-
"it cannot reconstruct caveat-preserving persisted ReCap authority"
|
|
3941
|
-
);
|
|
3942
|
-
}
|
|
3943
|
-
stagedRecap = exactRecap.map((entry) => ({
|
|
3944
|
-
service: entry.service,
|
|
3945
|
-
space: entry.space,
|
|
3946
|
-
path: entry.path,
|
|
3947
|
-
actions: [...entry.actions],
|
|
3948
|
-
caveats: cloneRecapCaveats(entry.caveats)
|
|
3949
|
-
}));
|
|
4017
|
+
stagedRecap = exactVerifiedRecap(verified);
|
|
3950
4018
|
const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
|
|
3951
4019
|
const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
|
|
3952
4020
|
if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
|
|
@@ -4750,7 +4818,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4750
4818
|
if (cached) return cached;
|
|
4751
4819
|
const response = await fetch(`${this.config.host}/info`);
|
|
4752
4820
|
if (!response.ok) {
|
|
4753
|
-
throw
|
|
4821
|
+
throw await httpResponseError(response, "Failed to fetch node info");
|
|
4754
4822
|
}
|
|
4755
4823
|
const info = await response.json();
|
|
4756
4824
|
if (typeof info.nodeId !== "string" || info.nodeId.length === 0) {
|
|
@@ -4800,9 +4868,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4800
4868
|
);
|
|
4801
4869
|
if (response.status === 404) return null;
|
|
4802
4870
|
if (!response.ok) {
|
|
4803
|
-
throw
|
|
4804
|
-
`Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
4805
|
-
);
|
|
4871
|
+
throw await httpResponseError(response, `Failed to fetch encryption network ${networkId}`);
|
|
4806
4872
|
}
|
|
4807
4873
|
const body = await response.json();
|
|
4808
4874
|
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
@@ -5235,7 +5301,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5235
5301
|
const activation = await activateSessionWithHost2(host, delegationSession.delegationHeader);
|
|
5236
5302
|
assertOwnerGraphActive();
|
|
5237
5303
|
if (!activation.success) {
|
|
5238
|
-
throw
|
|
5304
|
+
throw Object.assign(
|
|
5305
|
+
new Error(`Owner delegation import failed: ${activation.status} ${activation.error ?? ""}`.trim()),
|
|
5306
|
+
{ cause: activation }
|
|
5307
|
+
);
|
|
5239
5308
|
}
|
|
5240
5309
|
const delegation = {
|
|
5241
5310
|
cid: delegationSession.delegationCid,
|
|
@@ -5300,7 +5369,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5300
5369
|
headers: { accept: "application/json", "content-type": "application/json", authorization },
|
|
5301
5370
|
body: canonicalizeEncryptionJson(request)
|
|
5302
5371
|
});
|
|
5303
|
-
if (!response.ok) throw
|
|
5372
|
+
if (!response.ok) throw await httpResponseError(response, "V3 share delivery authorization failed");
|
|
5304
5373
|
const verified = validateShareDeliveryAuthorizationV3Bytes(new Uint8Array(await response.arrayBuffer()), {
|
|
5305
5374
|
request,
|
|
5306
5375
|
senderKeyDid: this.credentialHolderDid,
|
|
@@ -5522,9 +5591,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5522
5591
|
);
|
|
5523
5592
|
}
|
|
5524
5593
|
if (!response.ok) {
|
|
5525
|
-
throw
|
|
5526
|
-
`Failed to create encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
5527
|
-
);
|
|
5594
|
+
throw await httpResponseError(response, `Failed to create encryption network ${networkId}`);
|
|
5528
5595
|
}
|
|
5529
5596
|
const created = await response.json();
|
|
5530
5597
|
return created.descriptor;
|
|
@@ -5808,6 +5875,42 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5808
5875
|
(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"))
|
|
5809
5876
|
);
|
|
5810
5877
|
}
|
|
5878
|
+
/**
|
|
5879
|
+
* Verify a wallet-signed session grant (a CACAO built from a SIWE and its
|
|
5880
|
+
* EIP-191 signature) issued to this instance's live session key. The
|
|
5881
|
+
* verifier rebuilds the CACAO from `siwe` and `signature`, so the grant is
|
|
5882
|
+
* accepted only when its authorization bytes and CID are exactly that CACAO,
|
|
5883
|
+
* its signer is `address`, its audience is this session key, it is valid
|
|
5884
|
+
* now, and its ReCap covers `spaceId` or names only raw encryption networks
|
|
5885
|
+
* (a decrypt-only grant has no space). Returns the signed ReCap and expiry.
|
|
5886
|
+
* Bindings without `validateSessionGrant` fall back to the session verifier,
|
|
5887
|
+
* which also refuses decrypt-only grants.
|
|
5888
|
+
*/
|
|
5889
|
+
verifySessionGrant(grant) {
|
|
5890
|
+
const bindings = this.wasmBindings;
|
|
5891
|
+
const validate = typeof bindings.validateSessionGrant === "function" ? bindings.validateSessionGrant.bind(bindings) : bindings.validatePersistedSession?.bind(bindings);
|
|
5892
|
+
if (validate === void 0) {
|
|
5893
|
+
throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
|
|
5894
|
+
}
|
|
5895
|
+
const verified = validate({
|
|
5896
|
+
delegationHeader: { Authorization: grant.delegationHeader.Authorization },
|
|
5897
|
+
delegationCid: grant.delegationCid,
|
|
5898
|
+
spaceId: grant.spaceId,
|
|
5899
|
+
jwk: this.sessionKeyJwk,
|
|
5900
|
+
address: canonicalizeAddress2(grant.address),
|
|
5901
|
+
chainId: grant.chainId,
|
|
5902
|
+
siwe: grant.siwe,
|
|
5903
|
+
signature: grant.signature
|
|
5904
|
+
});
|
|
5905
|
+
if (typeof verified.expiresAt !== "string") {
|
|
5906
|
+
throw new Error("Session grant has no signed expiry.");
|
|
5907
|
+
}
|
|
5908
|
+
const expiresAt = new Date(verified.expiresAt);
|
|
5909
|
+
if (!Number.isFinite(expiresAt.getTime())) {
|
|
5910
|
+
throw new Error("Session grant has an invalid signed expiry.");
|
|
5911
|
+
}
|
|
5912
|
+
return { recap: exactVerifiedRecap(verified), expiresAt };
|
|
5913
|
+
}
|
|
5811
5914
|
/**
|
|
5812
5915
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
5813
5916
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -5832,8 +5935,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5832
5935
|
delegation.delegationHeader
|
|
5833
5936
|
);
|
|
5834
5937
|
if (!activateResult.success) {
|
|
5835
|
-
throw
|
|
5836
|
-
`Failed to activate runtime permission delegation: ${activateResult
|
|
5938
|
+
throw Object.assign(
|
|
5939
|
+
new Error(`Failed to activate runtime permission delegation: ${describeHostFailure2(activateResult)}`),
|
|
5940
|
+
{ cause: activateResult }
|
|
5837
5941
|
);
|
|
5838
5942
|
}
|
|
5839
5943
|
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
@@ -5929,8 +6033,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5929
6033
|
delegatedSession.delegationHeader
|
|
5930
6034
|
);
|
|
5931
6035
|
if (!activateResult.success) {
|
|
5932
|
-
throw
|
|
5933
|
-
`Failed to activate runtime permission delegation: ${activateResult
|
|
6036
|
+
throw Object.assign(
|
|
6037
|
+
new Error(`Failed to activate runtime permission delegation: ${describeHostFailure2(activateResult)}`),
|
|
6038
|
+
{ cause: activateResult }
|
|
5934
6039
|
);
|
|
5935
6040
|
}
|
|
5936
6041
|
const delegation = this.runtimeDelegationFromSession(
|
|
@@ -5938,7 +6043,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5938
6043
|
delegatedEntries,
|
|
5939
6044
|
spaceId,
|
|
5940
6045
|
session,
|
|
5941
|
-
expiresAt
|
|
6046
|
+
expiresAt,
|
|
6047
|
+
{ siwe: prepared.siwe, signature }
|
|
5942
6048
|
);
|
|
5943
6049
|
this.runtimePermissionGrants.push({
|
|
5944
6050
|
session: {
|
|
@@ -6115,7 +6221,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6115
6221
|
delegationSession.delegationHeader
|
|
6116
6222
|
);
|
|
6117
6223
|
if (!activateResult.success) {
|
|
6118
|
-
throw
|
|
6224
|
+
throw Object.assign(
|
|
6225
|
+
new Error(`Failed to activate public space delegation: ${describeHostFailure2(activateResult)}`),
|
|
6226
|
+
{ cause: activateResult }
|
|
6227
|
+
);
|
|
6119
6228
|
}
|
|
6120
6229
|
if (this._capabilityRegistry && this.session) {
|
|
6121
6230
|
const sessionKey = {
|
|
@@ -6468,8 +6577,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6468
6577
|
delegationHeader
|
|
6469
6578
|
);
|
|
6470
6579
|
if (!activateResult.success) {
|
|
6471
|
-
throw
|
|
6472
|
-
`Failed to activate delegation with host: ${activateResult
|
|
6580
|
+
throw Object.assign(
|
|
6581
|
+
new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
|
|
6582
|
+
{ cause: activateResult }
|
|
6473
6583
|
);
|
|
6474
6584
|
}
|
|
6475
6585
|
return {
|
|
@@ -6505,8 +6615,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6505
6615
|
delegationHeader
|
|
6506
6616
|
);
|
|
6507
6617
|
if (!activateResult.success) {
|
|
6508
|
-
throw
|
|
6509
|
-
`Failed to activate delegation with host: ${activateResult
|
|
6618
|
+
throw Object.assign(
|
|
6619
|
+
new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
|
|
6620
|
+
{ cause: activateResult }
|
|
6510
6621
|
);
|
|
6511
6622
|
}
|
|
6512
6623
|
return {
|
|
@@ -6719,7 +6830,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6719
6830
|
}
|
|
6720
6831
|
return grants;
|
|
6721
6832
|
}
|
|
6722
|
-
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
|
|
6833
|
+
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt, siweProof) {
|
|
6723
6834
|
const resources = this.delegatedResourcesForEntries(entries, spaceId);
|
|
6724
6835
|
const primary = resources[0];
|
|
6725
6836
|
return {
|
|
@@ -6734,7 +6845,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6734
6845
|
delegateDID: session.verificationMethod,
|
|
6735
6846
|
ownerAddress: session.address,
|
|
6736
6847
|
chainId: session.chainId,
|
|
6737
|
-
host: this.config.host
|
|
6848
|
+
host: this.config.host,
|
|
6849
|
+
siweProof
|
|
6738
6850
|
};
|
|
6739
6851
|
}
|
|
6740
6852
|
runtimeGrantFromDelegation(delegation, session) {
|
|
@@ -6927,6 +7039,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6927
7039
|
if (grantedAction === requestedAction) {
|
|
6928
7040
|
return true;
|
|
6929
7041
|
}
|
|
7042
|
+
if (isExplicitOnlyAction(requestedAction)) {
|
|
7043
|
+
return false;
|
|
7044
|
+
}
|
|
6930
7045
|
if (grantedAction.endsWith("/*")) {
|
|
6931
7046
|
const prefix = grantedAction.slice(0, -2);
|
|
6932
7047
|
return requestedAction.startsWith(`${prefix}/`);
|
|
@@ -7113,7 +7228,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7113
7228
|
delegationSession.delegationHeader
|
|
7114
7229
|
);
|
|
7115
7230
|
if (!activateResult.success) {
|
|
7116
|
-
throw
|
|
7231
|
+
throw Object.assign(
|
|
7232
|
+
new Error(`Failed to activate delegation: ${describeHostFailure2(activateResult)}`),
|
|
7233
|
+
{ cause: activateResult }
|
|
7234
|
+
);
|
|
7117
7235
|
}
|
|
7118
7236
|
const result = {
|
|
7119
7237
|
cid: delegationSession.delegationCid,
|
|
@@ -7263,7 +7381,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7263
7381
|
invokerSession.delegationHeader
|
|
7264
7382
|
);
|
|
7265
7383
|
if (!activateResult.success) {
|
|
7266
|
-
throw
|
|
7384
|
+
throw Object.assign(
|
|
7385
|
+
new Error(`Failed to activate delegated session: ${describeHostFailure2(activateResult)}`),
|
|
7386
|
+
{ cause: activateResult }
|
|
7387
|
+
);
|
|
7267
7388
|
}
|
|
7268
7389
|
const session = {
|
|
7269
7390
|
address: mySession.address,
|
|
@@ -7380,7 +7501,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7380
7501
|
subDelegationSession.delegationHeader
|
|
7381
7502
|
);
|
|
7382
7503
|
if (!activateResult.success) {
|
|
7383
|
-
throw
|
|
7504
|
+
throw Object.assign(
|
|
7505
|
+
new Error(`Failed to activate sub-delegation: ${describeHostFailure2(activateResult)}`),
|
|
7506
|
+
{ cause: activateResult }
|
|
7507
|
+
);
|
|
7384
7508
|
}
|
|
7385
7509
|
return {
|
|
7386
7510
|
cid: subDelegationSession.delegationCid,
|
|
@@ -7445,6 +7569,16 @@ import {
|
|
|
7445
7569
|
decodeBase64Url,
|
|
7446
7570
|
principalDidEquals as principalDidEquals3
|
|
7447
7571
|
} from "@tinycloud/sdk-core";
|
|
7572
|
+
var RuntimeDelegationRejectedError = class extends Error {
|
|
7573
|
+
constructor(reason, message) {
|
|
7574
|
+
super(message);
|
|
7575
|
+
this.name = "RuntimeDelegationRejectedError";
|
|
7576
|
+
this.reason = reason;
|
|
7577
|
+
}
|
|
7578
|
+
};
|
|
7579
|
+
function reject(reason, message) {
|
|
7580
|
+
throw new RuntimeDelegationRejectedError(reason, message);
|
|
7581
|
+
}
|
|
7448
7582
|
var SERVICE_BY_SHORT_NAME = {
|
|
7449
7583
|
kv: "tinycloud.kv",
|
|
7450
7584
|
sql: "tinycloud.sql",
|
|
@@ -7591,6 +7725,15 @@ function signedAuthorityFromCompactUcan(authorization) {
|
|
|
7591
7725
|
abilities
|
|
7592
7726
|
));
|
|
7593
7727
|
}
|
|
7728
|
+
return canonicalSignedAuthority({
|
|
7729
|
+
issuer: payload.iss,
|
|
7730
|
+
audience: payload.aud,
|
|
7731
|
+
expiry,
|
|
7732
|
+
proofs: [...payload.prf],
|
|
7733
|
+
...remainingRedelegationDepth === void 0 ? {} : { remainingRedelegationDepth }
|
|
7734
|
+
}, permissions);
|
|
7735
|
+
}
|
|
7736
|
+
function canonicalSignedAuthority(signed, permissions) {
|
|
7594
7737
|
if (permissions.length === 0) {
|
|
7595
7738
|
throw new Error("Validated runtime delegation has no signed capabilities.");
|
|
7596
7739
|
}
|
|
@@ -7602,15 +7745,84 @@ function signedAuthorityFromCompactUcan(authorization) {
|
|
|
7602
7745
|
actions: [...permission.actions],
|
|
7603
7746
|
...permission.caveats === void 0 ? {} : { caveats: permission.caveats }
|
|
7604
7747
|
}));
|
|
7605
|
-
return {
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7748
|
+
return { ...signed, permissions: canonicalPermissions, resources: canonicalResources };
|
|
7749
|
+
}
|
|
7750
|
+
var RAW_ENCRYPTION_PREFIX = "urn:tinycloud:encryption:";
|
|
7751
|
+
var PKH_SPACE = /^tinycloud:pkh:eip155:(\d+):(0x[0-9a-fA-F]{40}):(.+)$/;
|
|
7752
|
+
function comparableSpace(space) {
|
|
7753
|
+
const match = PKH_SPACE.exec(space);
|
|
7754
|
+
return match ? `tinycloud:pkh:eip155:${match[1]}:${match[2].toLowerCase()}:${match[3]}` : space;
|
|
7755
|
+
}
|
|
7756
|
+
function signedAuthorityFromSessionGrant(node, delegation, authorization, cid) {
|
|
7757
|
+
const proof = delegation.siweProof;
|
|
7758
|
+
if (proof === void 0 || proof === null || typeof proof !== "object" || typeof proof.siwe !== "string" || typeof proof.signature !== "string") {
|
|
7759
|
+
reject(
|
|
7760
|
+
"proof_missing",
|
|
7761
|
+
"Runtime delegation is a wallet-signed session grant stored without its signed SIWE proof, so its authority cannot be verified."
|
|
7762
|
+
);
|
|
7763
|
+
}
|
|
7764
|
+
if (typeof node.verifySessionGrant !== "function") {
|
|
7765
|
+
reject("unsupported", "This runtime cannot verify wallet-signed session grants.");
|
|
7766
|
+
}
|
|
7767
|
+
const owner = `did:pkh:eip155:${delegation.chainId}:${delegation.ownerAddress}`;
|
|
7768
|
+
if (typeof node.did !== "string" || !didPrincipalsMatch(owner, node.did)) {
|
|
7769
|
+
reject("owner_mismatch", "Runtime delegation is not signed by this session's owner.");
|
|
7770
|
+
}
|
|
7771
|
+
let verified;
|
|
7772
|
+
try {
|
|
7773
|
+
verified = node.verifySessionGrant({
|
|
7774
|
+
delegationHeader: { Authorization: authorization },
|
|
7775
|
+
delegationCid: cid,
|
|
7776
|
+
spaceId: delegation.spaceId,
|
|
7777
|
+
address: delegation.ownerAddress,
|
|
7778
|
+
chainId: delegation.chainId,
|
|
7779
|
+
siwe: proof.siwe,
|
|
7780
|
+
signature: proof.signature
|
|
7781
|
+
});
|
|
7782
|
+
} catch (error) {
|
|
7783
|
+
reject("proof_invalid", error instanceof Error ? error.message : String(error));
|
|
7784
|
+
}
|
|
7785
|
+
const space = comparableSpace(delegation.spaceId);
|
|
7786
|
+
const spaceOwner = PKH_SPACE.exec(delegation.spaceId);
|
|
7787
|
+
if (!spaceOwner || !didPrincipalsMatch(`did:pkh:eip155:${spaceOwner[1]}:${spaceOwner[2]}`, owner)) {
|
|
7788
|
+
reject("authority_invalid", "Runtime delegation space is not owned by its signer.");
|
|
7789
|
+
}
|
|
7790
|
+
const groups = /* @__PURE__ */ new Map();
|
|
7791
|
+
for (const entry of verified.recap) {
|
|
7792
|
+
const service = entry.service.startsWith("tinycloud.") ? entry.service : `tinycloud.${entry.service}`;
|
|
7793
|
+
const raw = entry.space === "encryption";
|
|
7794
|
+
if (raw) {
|
|
7795
|
+
if (service !== "tinycloud.encryption" || !entry.path.startsWith(RAW_ENCRYPTION_PREFIX)) {
|
|
7796
|
+
reject("authority_invalid", "Runtime delegation has an unsupported signed raw resource.");
|
|
7797
|
+
}
|
|
7798
|
+
const networkOwner = entry.path.slice(RAW_ENCRYPTION_PREFIX.length, entry.path.lastIndexOf(":"));
|
|
7799
|
+
if (!didPrincipalsMatch(networkOwner, owner)) {
|
|
7800
|
+
reject("authority_invalid", "Runtime delegation names an encryption network its signer does not own.");
|
|
7801
|
+
}
|
|
7802
|
+
} else if (service === "tinycloud.encryption" || comparableSpace(entry.space) !== space) {
|
|
7803
|
+
reject("authority_invalid", "Runtime delegation has signed authority outside its space.");
|
|
7804
|
+
}
|
|
7805
|
+
const caveats = signedCaveats(entry.caveats ?? []);
|
|
7806
|
+
const key = JSON.stringify([service, raw ? "" : entry.space, entry.path, canonicalizeRecapCaveats2(caveats)]);
|
|
7807
|
+
const group = groups.get(key) ?? {
|
|
7808
|
+
service,
|
|
7809
|
+
...raw ? {} : { space: entry.space },
|
|
7810
|
+
path: entry.path,
|
|
7811
|
+
actions: [],
|
|
7812
|
+
...caveats === void 0 ? {} : { caveats }
|
|
7813
|
+
};
|
|
7814
|
+
for (const action of entry.actions) {
|
|
7815
|
+
if (!group.actions.includes(action)) group.actions.push(action);
|
|
7816
|
+
}
|
|
7817
|
+
groups.set(key, group);
|
|
7818
|
+
}
|
|
7819
|
+
return canonicalSignedAuthority({
|
|
7820
|
+
issuer: owner,
|
|
7821
|
+
// The verifier bound the SIWE audience to this runtime's own session key.
|
|
7822
|
+
audience: node.sessionDid,
|
|
7823
|
+
expiry: verified.expiresAt,
|
|
7824
|
+
proofs: []
|
|
7825
|
+
}, [...groups.values()]);
|
|
7614
7826
|
}
|
|
7615
7827
|
function canonicalResourcesFromPortableDelegation(delegation) {
|
|
7616
7828
|
if (delegation.resources !== void 0 && delegation.resources.length > 0) {
|
|
@@ -7646,46 +7858,58 @@ function resourcesMatch(actual, expected) {
|
|
|
7646
7858
|
async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
7647
7859
|
const host = normalizedHost(options.host);
|
|
7648
7860
|
if (delegation.host !== void 0 && normalizedHost(delegation.host) !== host) {
|
|
7649
|
-
|
|
7861
|
+
reject(
|
|
7862
|
+
"host_mismatch",
|
|
7650
7863
|
`Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`
|
|
7651
7864
|
);
|
|
7652
7865
|
}
|
|
7653
7866
|
if (!(delegation.expiry instanceof Date) || Number.isNaN(delegation.expiry.getTime())) {
|
|
7654
|
-
|
|
7867
|
+
reject("authority_invalid", "Runtime delegation has an invalid expiry.");
|
|
7655
7868
|
}
|
|
7656
7869
|
if (delegation.expiry.getTime() <= Date.now()) {
|
|
7657
|
-
|
|
7870
|
+
reject("expired", "Runtime delegation is expired.");
|
|
7658
7871
|
}
|
|
7659
7872
|
const authorization = delegation.delegationHeader?.Authorization;
|
|
7660
7873
|
if (typeof authorization !== "string" || authorization.length === 0) {
|
|
7661
|
-
|
|
7874
|
+
reject("authority_invalid", "Runtime delegation is missing authorization bytes.");
|
|
7662
7875
|
}
|
|
7663
7876
|
const cid = node.computeDelegationCid(authorization);
|
|
7664
7877
|
if (cid !== delegation.cid) {
|
|
7665
|
-
|
|
7878
|
+
reject("cid_mismatch", "Runtime delegation CID does not match authorization bytes.");
|
|
7879
|
+
}
|
|
7880
|
+
const compact = authorizationWithoutBearer(authorization).split(".").length === 3;
|
|
7881
|
+
let signed;
|
|
7882
|
+
try {
|
|
7883
|
+
signed = compact ? signedAuthorityFromCompactUcan(authorization) : signedAuthorityFromSessionGrant(node, delegation, authorization, cid);
|
|
7884
|
+
} catch (error) {
|
|
7885
|
+
if (error instanceof RuntimeDelegationRejectedError) throw error;
|
|
7886
|
+
reject("authority_invalid", error instanceof Error ? error.message : String(error));
|
|
7666
7887
|
}
|
|
7667
|
-
const signed = signedAuthorityFromCompactUcan(authorization);
|
|
7668
7888
|
if (signed.expiry.getTime() <= Date.now()) {
|
|
7669
|
-
|
|
7889
|
+
reject("expired", "Runtime delegation is expired.");
|
|
7670
7890
|
}
|
|
7671
7891
|
if (delegation.expiry.getTime() !== signed.expiry.getTime()) {
|
|
7672
|
-
|
|
7892
|
+
reject("authority_invalid", "Runtime delegation expiry does not match signed authority.");
|
|
7673
7893
|
}
|
|
7674
7894
|
if (!didPrincipalsMatch(signed.audience, node.sessionDid)) {
|
|
7675
|
-
|
|
7895
|
+
reject(
|
|
7896
|
+
"audience_mismatch",
|
|
7676
7897
|
`Runtime delegation targets ${signed.audience} but this session is ${node.sessionDid}.`
|
|
7677
7898
|
);
|
|
7678
7899
|
}
|
|
7679
7900
|
if (!didPrincipalsMatch(delegation.delegateDID, signed.audience)) {
|
|
7680
|
-
|
|
7901
|
+
reject("audience_mismatch", "Runtime delegation audience does not match signed authority.");
|
|
7902
|
+
}
|
|
7903
|
+
if (compact && !resourcesMatch(canonicalResourcesFromPortableDelegation(delegation), signed.resources)) {
|
|
7904
|
+
reject("resources_mismatch", "Runtime delegation resources do not match signed authority.");
|
|
7681
7905
|
}
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
throw new Error("Runtime delegation resources do not match signed authority.");
|
|
7906
|
+
if (options.authorize !== void 0 && !options.authorize(signed.permissions)) {
|
|
7907
|
+
throw new Error("Runtime delegation is not authorized for this runtime.");
|
|
7685
7908
|
}
|
|
7686
7909
|
const previouslyInstalled = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
7687
7910
|
if (previouslyInstalled !== void 0 && previouslyInstalled.delegationHeader.Authorization !== authorization) {
|
|
7688
|
-
|
|
7911
|
+
reject(
|
|
7912
|
+
"cid_mismatch",
|
|
7689
7913
|
"A different authorization is already installed for this runtime delegation CID."
|
|
7690
7914
|
);
|
|
7691
7915
|
}
|
|
@@ -7715,22 +7939,28 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
|
7715
7939
|
})),
|
|
7716
7940
|
expiry: new Date(signed.expiry),
|
|
7717
7941
|
delegateDID: signed.audience,
|
|
7718
|
-
host
|
|
7942
|
+
host,
|
|
7943
|
+
...compact ? {} : { siweProof: delegation.siweProof }
|
|
7719
7944
|
};
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7945
|
+
try {
|
|
7946
|
+
if (node.isSessionOnly === true && typeof node.did === "string" && principalDidEquals3(node.did, node.sessionDid) && typeof node.useDelegation === "function") {
|
|
7947
|
+
await node.useDelegation(installedCandidate);
|
|
7948
|
+
} else {
|
|
7949
|
+
await node.useRuntimeDelegation(installedCandidate);
|
|
7950
|
+
}
|
|
7951
|
+
} catch (error) {
|
|
7952
|
+
reject("activation_rejected", error instanceof Error ? error.message : String(error));
|
|
7724
7953
|
}
|
|
7725
7954
|
const installed = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
7726
7955
|
if (!installed) {
|
|
7727
|
-
|
|
7956
|
+
reject("activation_rejected", "Runtime delegation activation did not install the validated authority.");
|
|
7728
7957
|
}
|
|
7729
7958
|
if (installed.delegationHeader.Authorization !== authorization || !didPrincipalsMatch(installed.delegateDID, signed.audience) || installed.expiry.getTime() !== signed.expiry.getTime() || !resourcesMatch(
|
|
7730
7959
|
canonicalResourcesFromPortableDelegation(installed),
|
|
7731
7960
|
signed.resources
|
|
7732
7961
|
) || normalizedHost(installed.host ?? "") !== host) {
|
|
7733
|
-
|
|
7962
|
+
reject(
|
|
7963
|
+
"activation_rejected",
|
|
7734
7964
|
"Runtime delegation activation installed authority that differs from the validated authority."
|
|
7735
7965
|
);
|
|
7736
7966
|
}
|