@tinycloud/node-sdk 3.1.0-beta.1 → 3.1.0-beta.11
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-Bm76PnOz.d.cts → core-CJiLya1s.d.cts} +80 -6
- package/dist/{core-Bm76PnOz.d.ts → core-CJiLya1s.d.ts} +80 -6
- package/dist/core.cjs +348 -137
- 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 +354 -138
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +406 -169
- 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 +412 -171
- 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) {
|
|
@@ -790,10 +794,12 @@ var NodeUserAuthorization = class {
|
|
|
790
794
|
return;
|
|
791
795
|
}
|
|
792
796
|
try {
|
|
793
|
-
const created =
|
|
794
|
-
if (!created) {
|
|
795
|
-
const err =
|
|
796
|
-
|
|
797
|
+
const created = await this.hostSpace();
|
|
798
|
+
if (!created.success) {
|
|
799
|
+
const err = Object.assign(
|
|
800
|
+
new Error(`Failed to create space ${primarySpaceId}: ${describeHostFailure(created)}`),
|
|
801
|
+
{ cause: created }
|
|
802
|
+
);
|
|
797
803
|
throw err;
|
|
798
804
|
}
|
|
799
805
|
} catch (error) {
|
|
@@ -809,8 +815,9 @@ var NodeUserAuthorization = class {
|
|
|
809
815
|
this._tinyCloudSession.delegationHeader
|
|
810
816
|
);
|
|
811
817
|
if (!retryResult.success) {
|
|
812
|
-
const err =
|
|
813
|
-
`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 }
|
|
814
821
|
);
|
|
815
822
|
handler.onSpaceCreationFailed?.(creationContext, err);
|
|
816
823
|
throw err;
|
|
@@ -827,10 +834,12 @@ var NodeUserAuthorization = class {
|
|
|
827
834
|
return;
|
|
828
835
|
}
|
|
829
836
|
try {
|
|
830
|
-
const created =
|
|
831
|
-
if (!created) {
|
|
832
|
-
const err =
|
|
833
|
-
|
|
837
|
+
const created = await this.hostSpace();
|
|
838
|
+
if (!created.success) {
|
|
839
|
+
const err = Object.assign(
|
|
840
|
+
new Error(`Failed to create space ${primarySpaceId}: ${describeHostFailure(created)}`),
|
|
841
|
+
{ cause: created }
|
|
842
|
+
);
|
|
834
843
|
throw err;
|
|
835
844
|
}
|
|
836
845
|
} catch (error) {
|
|
@@ -846,8 +855,9 @@ var NodeUserAuthorization = class {
|
|
|
846
855
|
this._tinyCloudSession.delegationHeader
|
|
847
856
|
);
|
|
848
857
|
if (!retryResult.success) {
|
|
849
|
-
const err =
|
|
850
|
-
`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 }
|
|
851
861
|
);
|
|
852
862
|
handler.onSpaceCreationFailed?.(creationContext, err);
|
|
853
863
|
throw err;
|
|
@@ -855,7 +865,10 @@ var NodeUserAuthorization = class {
|
|
|
855
865
|
handler.onSpaceCreated?.(creationContext);
|
|
856
866
|
return;
|
|
857
867
|
}
|
|
858
|
-
throw
|
|
868
|
+
throw Object.assign(
|
|
869
|
+
new Error(`Failed to activate session: ${describeHostFailure(result)}`),
|
|
870
|
+
{ cause: result }
|
|
871
|
+
);
|
|
859
872
|
}
|
|
860
873
|
recordActivationSkippedSpaces(result, primarySpaceId) {
|
|
861
874
|
if (result.success) {
|
|
@@ -1645,14 +1658,14 @@ var NodeUserAuthorization = class {
|
|
|
1645
1658
|
* Request signature via event emitter with timeout.
|
|
1646
1659
|
*/
|
|
1647
1660
|
requestSignatureViaEmitter(request, emitter, timeout) {
|
|
1648
|
-
return new Promise((resolve,
|
|
1661
|
+
return new Promise((resolve, reject2) => {
|
|
1649
1662
|
const timeoutId = setTimeout(() => {
|
|
1650
|
-
|
|
1663
|
+
reject2(new Error("Sign request timed out"));
|
|
1651
1664
|
}, timeout);
|
|
1652
1665
|
const respond = async (response) => {
|
|
1653
1666
|
clearTimeout(timeoutId);
|
|
1654
1667
|
if (!response.approved) {
|
|
1655
|
-
|
|
1668
|
+
reject2(
|
|
1656
1669
|
new Error(response.reason ?? "Sign request rejected via emitter")
|
|
1657
1670
|
);
|
|
1658
1671
|
} else {
|
|
@@ -1670,6 +1683,7 @@ import {
|
|
|
1670
1683
|
TinyCloud,
|
|
1671
1684
|
activateSessionWithHost as activateSessionWithHost2,
|
|
1672
1685
|
authorizationVerdictOf,
|
|
1686
|
+
httpResponseError,
|
|
1673
1687
|
KVService as KVService2,
|
|
1674
1688
|
SQLService as SQLService2,
|
|
1675
1689
|
DuckDbService as DuckDbService2,
|
|
@@ -1703,6 +1717,7 @@ import {
|
|
|
1703
1717
|
recapCaveatsEqual,
|
|
1704
1718
|
expandPermissionEntries as expandPermissionEntriesCore,
|
|
1705
1719
|
isCapabilitySubset,
|
|
1720
|
+
isExplicitOnlyAction,
|
|
1706
1721
|
parseRecapCapabilities,
|
|
1707
1722
|
SERVICE_LONG_TO_SHORT,
|
|
1708
1723
|
SERVICE_SHORT_TO_LONG,
|
|
@@ -1725,7 +1740,10 @@ import {
|
|
|
1725
1740
|
CREDENTIAL_INVITATION_REQUEST_DOMAIN,
|
|
1726
1741
|
validateShareDeliveryAuthorizationV3Bytes,
|
|
1727
1742
|
verifyEip191MessageSignature,
|
|
1728
|
-
signCompactUcanRootAuthorization
|
|
1743
|
+
signCompactUcanRootAuthorization,
|
|
1744
|
+
isStorageFullError,
|
|
1745
|
+
STORAGE_FULL_MESSAGE,
|
|
1746
|
+
STORAGE_WRITE_TOO_LARGE_MESSAGE
|
|
1729
1747
|
} from "@tinycloud/sdk-core";
|
|
1730
1748
|
import {
|
|
1731
1749
|
parsePermissionHint
|
|
@@ -2313,6 +2331,25 @@ function jsonFromVerifier(value) {
|
|
|
2313
2331
|
function verifierRecapEntries(entries) {
|
|
2314
2332
|
return entries.map((entry) => Array.isArray(entry.caveats) ? { ...entry, caveats: entry.caveats.map((caveat) => jsonFromVerifier(caveat)) } : entry);
|
|
2315
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
|
+
}
|
|
2316
2353
|
var ServiceGraphLifetime = class {
|
|
2317
2354
|
constructor(invokeFn, invokeAnyFn, fetchFn) {
|
|
2318
2355
|
this.invokeFn = invokeFn;
|
|
@@ -2408,7 +2445,7 @@ function persistedExpiry(value) {
|
|
|
2408
2445
|
function sameInstant(left, right) {
|
|
2409
2446
|
return left.getTime() === right.getTime();
|
|
2410
2447
|
}
|
|
2411
|
-
function
|
|
2448
|
+
function describeHostFailure2(result) {
|
|
2412
2449
|
const text = result.error?.trim();
|
|
2413
2450
|
return text ? `${result.status} - ${text}` : `${result.status}`;
|
|
2414
2451
|
}
|
|
@@ -3154,12 +3191,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3154
3191
|
if (!auth) {
|
|
3155
3192
|
throw new Error("Account bootstrap requires an active wallet session");
|
|
3156
3193
|
}
|
|
3194
|
+
const accountSpaceId = this.ownedSpaceId(ACCOUNT_REGISTRY_SPACE2);
|
|
3157
3195
|
const skipped = auth.lastActivationSkippedSpaceIds;
|
|
3196
|
+
if (skipped.includes(accountSpaceId)) return { action: "run", mode: "fresh" };
|
|
3158
3197
|
if (skipped.some((spaceId) => enshrinedSpaceIds.has(spaceId))) {
|
|
3159
|
-
return { action: "run", mode: "
|
|
3198
|
+
return { action: "run", mode: "repair" };
|
|
3160
3199
|
}
|
|
3161
3200
|
try {
|
|
3162
|
-
const accountSpaceId = this.ownedSpaceId(ACCOUNT_REGISTRY_SPACE2);
|
|
3163
3201
|
const markerPermission = {
|
|
3164
3202
|
service: "tinycloud.kv",
|
|
3165
3203
|
space: accountSpaceId,
|
|
@@ -3171,7 +3209,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3171
3209
|
}
|
|
3172
3210
|
const marker = await this.readBootstrapCompletionMarker();
|
|
3173
3211
|
if (marker.ok) {
|
|
3174
|
-
if (this.isAcceptedBootstrapCompletionMarker(marker.data.data)) {
|
|
3212
|
+
if (this.isAcceptedBootstrapCompletionMarker(marker.data.data, steps.map((step) => step.id))) {
|
|
3175
3213
|
return { action: "skip" };
|
|
3176
3214
|
}
|
|
3177
3215
|
return { action: "run", mode: "repair" };
|
|
@@ -3215,15 +3253,22 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3215
3253
|
marker
|
|
3216
3254
|
);
|
|
3217
3255
|
if (!written.ok) {
|
|
3218
|
-
throw
|
|
3256
|
+
throw Object.assign(
|
|
3257
|
+
new Error(`Failed to write bootstrap completion marker: ${written.error.message}`),
|
|
3258
|
+
{ cause: written.error }
|
|
3259
|
+
);
|
|
3219
3260
|
}
|
|
3220
3261
|
}
|
|
3221
|
-
|
|
3262
|
+
/**
|
|
3263
|
+
* A marker written before a canonical space was added triggers one bounded repair.
|
|
3264
|
+
*/
|
|
3265
|
+
isAcceptedBootstrapCompletionMarker(value, canonicalStepIds) {
|
|
3222
3266
|
if (typeof value !== "object" || value === null) return false;
|
|
3223
3267
|
const entries = Object.entries(value);
|
|
3224
3268
|
const markerValue = (name) => entries.find(([key]) => key === name)?.[1];
|
|
3225
3269
|
const version = markerValue("v");
|
|
3226
|
-
|
|
3270
|
+
const stepIds = markerValue("stepIds");
|
|
3271
|
+
return typeof version === "number" && Number.isInteger(version) && ACCEPTED_MARKER_VERSIONS.some((acceptedVersion) => acceptedVersion === version) && Array.isArray(stepIds) && canonicalStepIds.every((stepId) => stepIds.includes(stepId));
|
|
3227
3272
|
}
|
|
3228
3273
|
markerReadIsUnhostedSpace(meta) {
|
|
3229
3274
|
return meta?.status === 404;
|
|
@@ -3261,17 +3306,23 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3261
3306
|
rawAbilities
|
|
3262
3307
|
});
|
|
3263
3308
|
} catch (err) {
|
|
3264
|
-
throw
|
|
3265
|
-
|
|
3309
|
+
throw Object.assign(
|
|
3310
|
+
new Error(
|
|
3311
|
+
`Account bootstrap aborted: signature rejected for space "${step.space}". Cause: ${err instanceof Error ? err.message : String(err)}`
|
|
3312
|
+
),
|
|
3313
|
+
{ cause: err }
|
|
3266
3314
|
);
|
|
3267
3315
|
}
|
|
3268
3316
|
sessions.set(step.space, session);
|
|
3269
3317
|
}
|
|
3270
3318
|
for (const step of steps) {
|
|
3271
3319
|
if (step.kind !== "host") continue;
|
|
3272
|
-
const hosted = await auth.
|
|
3273
|
-
if (!hosted) {
|
|
3274
|
-
throw
|
|
3320
|
+
const hosted = await auth.hostOwnedSpaceResult(step.spaceId, "bootstrap-host");
|
|
3321
|
+
if (!hosted.success) {
|
|
3322
|
+
throw Object.assign(
|
|
3323
|
+
new Error(`Failed to host bootstrap space ${step.spaceId}: ${describeHostFailure2(hosted)}`),
|
|
3324
|
+
{ cause: hosted }
|
|
3325
|
+
);
|
|
3275
3326
|
}
|
|
3276
3327
|
}
|
|
3277
3328
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
@@ -3283,8 +3334,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3283
3334
|
}
|
|
3284
3335
|
const activated = await activateSessionWithHost2(host, session.delegationHeader);
|
|
3285
3336
|
if (!activated.success || activated.skipped?.includes(step.spaceId)) {
|
|
3286
|
-
throw
|
|
3287
|
-
|
|
3337
|
+
throw Object.assign(
|
|
3338
|
+
new Error(
|
|
3339
|
+
`Failed to activate bootstrap session for ${step.spaceId}: ${activated.success ? "space was skipped" : describeHostFailure2(activated)}`
|
|
3340
|
+
),
|
|
3341
|
+
{ cause: activated }
|
|
3288
3342
|
);
|
|
3289
3343
|
}
|
|
3290
3344
|
this.registerBootstrapRuntimeGrant(
|
|
@@ -3297,33 +3351,43 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3297
3351
|
if (step.kind === "account-index-schema") {
|
|
3298
3352
|
const ensured = await this.account.index.ensure();
|
|
3299
3353
|
if (!ensured.ok) {
|
|
3300
|
-
throw new Error(`Failed to create account index schema: ${ensured.error.message}`);
|
|
3354
|
+
throw Object.assign(new Error(`Failed to create account index schema: ${ensured.error.message}`), { cause: ensured.error });
|
|
3301
3355
|
}
|
|
3302
3356
|
}
|
|
3303
3357
|
if (step.kind === "seed-spaces") {
|
|
3304
|
-
const
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3358
|
+
const inputs = step.spaces.map((space) => ({
|
|
3359
|
+
spaceId: space.spaceId,
|
|
3360
|
+
name: space.name,
|
|
3361
|
+
ownerDid: this.did,
|
|
3362
|
+
type: "owned",
|
|
3363
|
+
permissions: ["*"],
|
|
3364
|
+
status: "active"
|
|
3365
|
+
}));
|
|
3366
|
+
if (mode === "fresh") {
|
|
3367
|
+
const registered = await this.account.spaces.registerBatch(inputs);
|
|
3368
|
+
if (!registered.ok) {
|
|
3369
|
+
throw Object.assign(
|
|
3370
|
+
new Error(`Failed to seed account spaces: ${registered.error.message}`),
|
|
3371
|
+
{ cause: registered.error }
|
|
3372
|
+
);
|
|
3373
|
+
}
|
|
3374
|
+
const batchError = registered.data.recoveredFromBatchError;
|
|
3375
|
+
if (batchError) {
|
|
3376
|
+
warnings.push({
|
|
3377
|
+
stepId: step.id,
|
|
3378
|
+
kind: "batch-write-reconciled",
|
|
3379
|
+
code: batchError.code,
|
|
3380
|
+
message: batchError.message
|
|
3381
|
+
});
|
|
3382
|
+
}
|
|
3383
|
+
} else {
|
|
3384
|
+
const ensured = await this.account.spaces.registerMissing(inputs);
|
|
3385
|
+
if (!ensured.ok) {
|
|
3386
|
+
throw Object.assign(
|
|
3387
|
+
new Error(`Failed to seed account spaces: ${ensured.error.message}`),
|
|
3388
|
+
{ cause: ensured.error }
|
|
3389
|
+
);
|
|
3390
|
+
}
|
|
3327
3391
|
}
|
|
3328
3392
|
}
|
|
3329
3393
|
if (step.kind === "seed-applications") {
|
|
@@ -3334,7 +3398,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3334
3398
|
{ assumeUnregistered: true }
|
|
3335
3399
|
);
|
|
3336
3400
|
if (!registered.ok) {
|
|
3337
|
-
throw new Error(`Failed to seed bootstrap applications: ${registered.error.message}`);
|
|
3401
|
+
throw Object.assign(new Error(`Failed to seed bootstrap applications: ${registered.error.message}`), { cause: registered.error });
|
|
3338
3402
|
}
|
|
3339
3403
|
}
|
|
3340
3404
|
if (step.kind === "encryption-network-create") {
|
|
@@ -3352,8 +3416,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3352
3416
|
]
|
|
3353
3417
|
});
|
|
3354
3418
|
if (!migrated.ok) {
|
|
3355
|
-
throw
|
|
3356
|
-
`Failed to create secret_records schema: ${migrated.error.message}`
|
|
3419
|
+
throw Object.assign(
|
|
3420
|
+
new Error(`Failed to create secret_records schema: ${migrated.error.message}`),
|
|
3421
|
+
{ cause: migrated.error }
|
|
3357
3422
|
);
|
|
3358
3423
|
}
|
|
3359
3424
|
}
|
|
@@ -3620,6 +3685,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3620
3685
|
);
|
|
3621
3686
|
return;
|
|
3622
3687
|
}
|
|
3688
|
+
if (isStorageFullError(error) || isStorageFullError(error?.cause) || message.includes(STORAGE_FULL_MESSAGE) || message.includes(STORAGE_WRITE_TOO_LARGE_MESSAGE)) {
|
|
3689
|
+
console.warn("TinyCloud account registry sync stopped: storage is full", error);
|
|
3690
|
+
return;
|
|
3691
|
+
}
|
|
3623
3692
|
lastError = error;
|
|
3624
3693
|
if (attempt < delays.length - 1) {
|
|
3625
3694
|
await new Promise((resolve) => setTimeout(resolve, delays[attempt]));
|
|
@@ -3694,14 +3763,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3694
3763
|
}
|
|
3695
3764
|
if (!activation.success && activation.status !== 404) {
|
|
3696
3765
|
throw Object.assign(
|
|
3697
|
-
new Error(`Failed to check owned space ${spaceId}: ${
|
|
3766
|
+
new Error(`Failed to check owned space ${spaceId}: ${describeHostFailure2(activation)}`),
|
|
3698
3767
|
{ cause: activation }
|
|
3699
3768
|
);
|
|
3700
3769
|
}
|
|
3701
3770
|
const created = await this.auth.hostOwnedSpaceResult(spaceId);
|
|
3702
3771
|
if (!created.success) {
|
|
3703
3772
|
throw Object.assign(
|
|
3704
|
-
new Error(`Failed to create owned space ${spaceId}: ${
|
|
3773
|
+
new Error(`Failed to create owned space ${spaceId}: ${describeHostFailure2(created)}`),
|
|
3705
3774
|
{ cause: created }
|
|
3706
3775
|
);
|
|
3707
3776
|
}
|
|
@@ -3710,7 +3779,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3710
3779
|
if (!retry.success || retry.skipped?.includes(spaceId)) {
|
|
3711
3780
|
throw Object.assign(
|
|
3712
3781
|
new Error(
|
|
3713
|
-
`Failed to activate session after creating owned space ${spaceId}: ${retry.success ? "space was skipped" :
|
|
3782
|
+
`Failed to activate session after creating owned space ${spaceId}: ${retry.success ? "space was skipped" : describeHostFailure2(retry)}`
|
|
3714
3783
|
),
|
|
3715
3784
|
{ cause: retry }
|
|
3716
3785
|
);
|
|
@@ -3747,19 +3816,23 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3747
3816
|
if (!host) {
|
|
3748
3817
|
throw new Error("Owned space hosting requires a TinyCloud host");
|
|
3749
3818
|
}
|
|
3750
|
-
const hosted = await this.auth.
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3819
|
+
const hosted = await this.auth.hostOwnedSpaceResult(spaceId);
|
|
3820
|
+
if (!hosted.success) {
|
|
3821
|
+
throw Object.assign(
|
|
3822
|
+
new Error(`Failed to host owned space ${spaceId}: ${describeHostFailure2(hosted)}`),
|
|
3823
|
+
{ cause: hosted }
|
|
3824
|
+
);
|
|
3755
3825
|
}
|
|
3756
3826
|
const activation = await activateSessionWithHost2(
|
|
3757
3827
|
host,
|
|
3758
3828
|
session.delegationHeader
|
|
3759
3829
|
);
|
|
3760
3830
|
if (!activation.success || activation.skipped?.includes(spaceId)) {
|
|
3761
|
-
throw
|
|
3762
|
-
|
|
3831
|
+
throw Object.assign(
|
|
3832
|
+
new Error(
|
|
3833
|
+
`Failed to activate session for owned space ${spaceId}: ${activation.success ? "space was skipped" : describeHostFailure2(activation)}`
|
|
3834
|
+
),
|
|
3835
|
+
{ cause: activation }
|
|
3763
3836
|
);
|
|
3764
3837
|
}
|
|
3765
3838
|
try {
|
|
@@ -3955,23 +4028,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3955
4028
|
throw new InvalidRestoredSessionError(error instanceof Error ? error.message : String(error));
|
|
3956
4029
|
}
|
|
3957
4030
|
})();
|
|
3958
|
-
|
|
3959
|
-
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
3960
|
-
(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(
|
|
3961
|
-
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
3962
|
-
)
|
|
3963
|
-
)) {
|
|
3964
|
-
throw new UnsupportedSessionRestoreError(
|
|
3965
|
-
"it cannot reconstruct caveat-preserving persisted ReCap authority"
|
|
3966
|
-
);
|
|
3967
|
-
}
|
|
3968
|
-
stagedRecap = exactRecap.map((entry) => ({
|
|
3969
|
-
service: entry.service,
|
|
3970
|
-
space: entry.space,
|
|
3971
|
-
path: entry.path,
|
|
3972
|
-
actions: [...entry.actions],
|
|
3973
|
-
caveats: cloneRecapCaveats(entry.caveats)
|
|
3974
|
-
}));
|
|
4031
|
+
stagedRecap = exactVerifiedRecap(verified);
|
|
3975
4032
|
const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
|
|
3976
4033
|
const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
|
|
3977
4034
|
if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
|
|
@@ -4775,7 +4832,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4775
4832
|
if (cached) return cached;
|
|
4776
4833
|
const response = await fetch(`${this.config.host}/info`);
|
|
4777
4834
|
if (!response.ok) {
|
|
4778
|
-
throw
|
|
4835
|
+
throw await httpResponseError(response, "Failed to fetch node info");
|
|
4779
4836
|
}
|
|
4780
4837
|
const info = await response.json();
|
|
4781
4838
|
if (typeof info.nodeId !== "string" || info.nodeId.length === 0) {
|
|
@@ -4825,9 +4882,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4825
4882
|
);
|
|
4826
4883
|
if (response.status === 404) return null;
|
|
4827
4884
|
if (!response.ok) {
|
|
4828
|
-
throw
|
|
4829
|
-
`Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
4830
|
-
);
|
|
4885
|
+
throw await httpResponseError(response, `Failed to fetch encryption network ${networkId}`);
|
|
4831
4886
|
}
|
|
4832
4887
|
const body = await response.json();
|
|
4833
4888
|
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
@@ -5328,7 +5383,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5328
5383
|
headers: { accept: "application/json", "content-type": "application/json", authorization },
|
|
5329
5384
|
body: canonicalizeEncryptionJson(request)
|
|
5330
5385
|
});
|
|
5331
|
-
if (!response.ok) throw
|
|
5386
|
+
if (!response.ok) throw await httpResponseError(response, "V3 share delivery authorization failed");
|
|
5332
5387
|
const verified = validateShareDeliveryAuthorizationV3Bytes(new Uint8Array(await response.arrayBuffer()), {
|
|
5333
5388
|
request,
|
|
5334
5389
|
senderKeyDid: this.credentialHolderDid,
|
|
@@ -5550,9 +5605,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5550
5605
|
);
|
|
5551
5606
|
}
|
|
5552
5607
|
if (!response.ok) {
|
|
5553
|
-
throw
|
|
5554
|
-
`Failed to create encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
5555
|
-
);
|
|
5608
|
+
throw await httpResponseError(response, `Failed to create encryption network ${networkId}`);
|
|
5556
5609
|
}
|
|
5557
5610
|
const created = await response.json();
|
|
5558
5611
|
return created.descriptor;
|
|
@@ -5836,6 +5889,42 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5836
5889
|
(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"))
|
|
5837
5890
|
);
|
|
5838
5891
|
}
|
|
5892
|
+
/**
|
|
5893
|
+
* Verify a wallet-signed session grant (a CACAO built from a SIWE and its
|
|
5894
|
+
* EIP-191 signature) issued to this instance's live session key. The
|
|
5895
|
+
* verifier rebuilds the CACAO from `siwe` and `signature`, so the grant is
|
|
5896
|
+
* accepted only when its authorization bytes and CID are exactly that CACAO,
|
|
5897
|
+
* its signer is `address`, its audience is this session key, it is valid
|
|
5898
|
+
* now, and its ReCap covers `spaceId` or names only raw encryption networks
|
|
5899
|
+
* (a decrypt-only grant has no space). Returns the signed ReCap and expiry.
|
|
5900
|
+
* Bindings without `validateSessionGrant` fall back to the session verifier,
|
|
5901
|
+
* which also refuses decrypt-only grants.
|
|
5902
|
+
*/
|
|
5903
|
+
verifySessionGrant(grant) {
|
|
5904
|
+
const bindings = this.wasmBindings;
|
|
5905
|
+
const validate = typeof bindings.validateSessionGrant === "function" ? bindings.validateSessionGrant.bind(bindings) : bindings.validatePersistedSession?.bind(bindings);
|
|
5906
|
+
if (validate === void 0) {
|
|
5907
|
+
throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
|
|
5908
|
+
}
|
|
5909
|
+
const verified = validate({
|
|
5910
|
+
delegationHeader: { Authorization: grant.delegationHeader.Authorization },
|
|
5911
|
+
delegationCid: grant.delegationCid,
|
|
5912
|
+
spaceId: grant.spaceId,
|
|
5913
|
+
jwk: this.sessionKeyJwk,
|
|
5914
|
+
address: canonicalizeAddress2(grant.address),
|
|
5915
|
+
chainId: grant.chainId,
|
|
5916
|
+
siwe: grant.siwe,
|
|
5917
|
+
signature: grant.signature
|
|
5918
|
+
});
|
|
5919
|
+
if (typeof verified.expiresAt !== "string") {
|
|
5920
|
+
throw new Error("Session grant has no signed expiry.");
|
|
5921
|
+
}
|
|
5922
|
+
const expiresAt = new Date(verified.expiresAt);
|
|
5923
|
+
if (!Number.isFinite(expiresAt.getTime())) {
|
|
5924
|
+
throw new Error("Session grant has an invalid signed expiry.");
|
|
5925
|
+
}
|
|
5926
|
+
return { recap: exactVerifiedRecap(verified), expiresAt };
|
|
5927
|
+
}
|
|
5839
5928
|
/**
|
|
5840
5929
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
5841
5930
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -5860,8 +5949,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5860
5949
|
delegation.delegationHeader
|
|
5861
5950
|
);
|
|
5862
5951
|
if (!activateResult.success) {
|
|
5863
|
-
throw
|
|
5864
|
-
`Failed to activate runtime permission delegation: ${activateResult
|
|
5952
|
+
throw Object.assign(
|
|
5953
|
+
new Error(`Failed to activate runtime permission delegation: ${describeHostFailure2(activateResult)}`),
|
|
5954
|
+
{ cause: activateResult }
|
|
5865
5955
|
);
|
|
5866
5956
|
}
|
|
5867
5957
|
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
@@ -5957,8 +6047,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5957
6047
|
delegatedSession.delegationHeader
|
|
5958
6048
|
);
|
|
5959
6049
|
if (!activateResult.success) {
|
|
5960
|
-
throw
|
|
5961
|
-
`Failed to activate runtime permission delegation: ${activateResult
|
|
6050
|
+
throw Object.assign(
|
|
6051
|
+
new Error(`Failed to activate runtime permission delegation: ${describeHostFailure2(activateResult)}`),
|
|
6052
|
+
{ cause: activateResult }
|
|
5962
6053
|
);
|
|
5963
6054
|
}
|
|
5964
6055
|
const delegation = this.runtimeDelegationFromSession(
|
|
@@ -5966,7 +6057,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5966
6057
|
delegatedEntries,
|
|
5967
6058
|
spaceId,
|
|
5968
6059
|
session,
|
|
5969
|
-
expiresAt
|
|
6060
|
+
expiresAt,
|
|
6061
|
+
{ siwe: prepared.siwe, signature }
|
|
5970
6062
|
);
|
|
5971
6063
|
this.runtimePermissionGrants.push({
|
|
5972
6064
|
session: {
|
|
@@ -6143,7 +6235,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6143
6235
|
delegationSession.delegationHeader
|
|
6144
6236
|
);
|
|
6145
6237
|
if (!activateResult.success) {
|
|
6146
|
-
throw
|
|
6238
|
+
throw Object.assign(
|
|
6239
|
+
new Error(`Failed to activate public space delegation: ${describeHostFailure2(activateResult)}`),
|
|
6240
|
+
{ cause: activateResult }
|
|
6241
|
+
);
|
|
6147
6242
|
}
|
|
6148
6243
|
if (this._capabilityRegistry && this.session) {
|
|
6149
6244
|
const sessionKey = {
|
|
@@ -6496,8 +6591,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6496
6591
|
delegationHeader
|
|
6497
6592
|
);
|
|
6498
6593
|
if (!activateResult.success) {
|
|
6499
|
-
throw
|
|
6500
|
-
`Failed to activate delegation with host: ${activateResult
|
|
6594
|
+
throw Object.assign(
|
|
6595
|
+
new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
|
|
6596
|
+
{ cause: activateResult }
|
|
6501
6597
|
);
|
|
6502
6598
|
}
|
|
6503
6599
|
return {
|
|
@@ -6533,8 +6629,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6533
6629
|
delegationHeader
|
|
6534
6630
|
);
|
|
6535
6631
|
if (!activateResult.success) {
|
|
6536
|
-
throw
|
|
6537
|
-
`Failed to activate delegation with host: ${activateResult
|
|
6632
|
+
throw Object.assign(
|
|
6633
|
+
new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
|
|
6634
|
+
{ cause: activateResult }
|
|
6538
6635
|
);
|
|
6539
6636
|
}
|
|
6540
6637
|
return {
|
|
@@ -6747,7 +6844,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6747
6844
|
}
|
|
6748
6845
|
return grants;
|
|
6749
6846
|
}
|
|
6750
|
-
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
|
|
6847
|
+
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt, siweProof) {
|
|
6751
6848
|
const resources = this.delegatedResourcesForEntries(entries, spaceId);
|
|
6752
6849
|
const primary = resources[0];
|
|
6753
6850
|
return {
|
|
@@ -6762,7 +6859,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6762
6859
|
delegateDID: session.verificationMethod,
|
|
6763
6860
|
ownerAddress: session.address,
|
|
6764
6861
|
chainId: session.chainId,
|
|
6765
|
-
host: this.config.host
|
|
6862
|
+
host: this.config.host,
|
|
6863
|
+
siweProof
|
|
6766
6864
|
};
|
|
6767
6865
|
}
|
|
6768
6866
|
runtimeGrantFromDelegation(delegation, session) {
|
|
@@ -6955,6 +7053,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6955
7053
|
if (grantedAction === requestedAction) {
|
|
6956
7054
|
return true;
|
|
6957
7055
|
}
|
|
7056
|
+
if (isExplicitOnlyAction(requestedAction)) {
|
|
7057
|
+
return false;
|
|
7058
|
+
}
|
|
6958
7059
|
if (grantedAction.endsWith("/*")) {
|
|
6959
7060
|
const prefix = grantedAction.slice(0, -2);
|
|
6960
7061
|
return requestedAction.startsWith(`${prefix}/`);
|
|
@@ -7141,7 +7242,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7141
7242
|
delegationSession.delegationHeader
|
|
7142
7243
|
);
|
|
7143
7244
|
if (!activateResult.success) {
|
|
7144
|
-
throw
|
|
7245
|
+
throw Object.assign(
|
|
7246
|
+
new Error(`Failed to activate delegation: ${describeHostFailure2(activateResult)}`),
|
|
7247
|
+
{ cause: activateResult }
|
|
7248
|
+
);
|
|
7145
7249
|
}
|
|
7146
7250
|
const result = {
|
|
7147
7251
|
cid: delegationSession.delegationCid,
|
|
@@ -7291,7 +7395,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7291
7395
|
invokerSession.delegationHeader
|
|
7292
7396
|
);
|
|
7293
7397
|
if (!activateResult.success) {
|
|
7294
|
-
throw
|
|
7398
|
+
throw Object.assign(
|
|
7399
|
+
new Error(`Failed to activate delegated session: ${describeHostFailure2(activateResult)}`),
|
|
7400
|
+
{ cause: activateResult }
|
|
7401
|
+
);
|
|
7295
7402
|
}
|
|
7296
7403
|
const session = {
|
|
7297
7404
|
address: mySession.address,
|
|
@@ -7408,7 +7515,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7408
7515
|
subDelegationSession.delegationHeader
|
|
7409
7516
|
);
|
|
7410
7517
|
if (!activateResult.success) {
|
|
7411
|
-
throw
|
|
7518
|
+
throw Object.assign(
|
|
7519
|
+
new Error(`Failed to activate sub-delegation: ${describeHostFailure2(activateResult)}`),
|
|
7520
|
+
{ cause: activateResult }
|
|
7521
|
+
);
|
|
7412
7522
|
}
|
|
7413
7523
|
return {
|
|
7414
7524
|
cid: subDelegationSession.delegationCid,
|
|
@@ -7473,6 +7583,16 @@ import {
|
|
|
7473
7583
|
decodeBase64Url,
|
|
7474
7584
|
principalDidEquals as principalDidEquals3
|
|
7475
7585
|
} from "@tinycloud/sdk-core";
|
|
7586
|
+
var RuntimeDelegationRejectedError = class extends Error {
|
|
7587
|
+
constructor(reason, message) {
|
|
7588
|
+
super(message);
|
|
7589
|
+
this.name = "RuntimeDelegationRejectedError";
|
|
7590
|
+
this.reason = reason;
|
|
7591
|
+
}
|
|
7592
|
+
};
|
|
7593
|
+
function reject(reason, message) {
|
|
7594
|
+
throw new RuntimeDelegationRejectedError(reason, message);
|
|
7595
|
+
}
|
|
7476
7596
|
var SERVICE_BY_SHORT_NAME = {
|
|
7477
7597
|
kv: "tinycloud.kv",
|
|
7478
7598
|
sql: "tinycloud.sql",
|
|
@@ -7619,6 +7739,15 @@ function signedAuthorityFromCompactUcan(authorization) {
|
|
|
7619
7739
|
abilities
|
|
7620
7740
|
));
|
|
7621
7741
|
}
|
|
7742
|
+
return canonicalSignedAuthority({
|
|
7743
|
+
issuer: payload.iss,
|
|
7744
|
+
audience: payload.aud,
|
|
7745
|
+
expiry,
|
|
7746
|
+
proofs: [...payload.prf],
|
|
7747
|
+
...remainingRedelegationDepth === void 0 ? {} : { remainingRedelegationDepth }
|
|
7748
|
+
}, permissions);
|
|
7749
|
+
}
|
|
7750
|
+
function canonicalSignedAuthority(signed, permissions) {
|
|
7622
7751
|
if (permissions.length === 0) {
|
|
7623
7752
|
throw new Error("Validated runtime delegation has no signed capabilities.");
|
|
7624
7753
|
}
|
|
@@ -7630,15 +7759,84 @@ function signedAuthorityFromCompactUcan(authorization) {
|
|
|
7630
7759
|
actions: [...permission.actions],
|
|
7631
7760
|
...permission.caveats === void 0 ? {} : { caveats: permission.caveats }
|
|
7632
7761
|
}));
|
|
7633
|
-
return {
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7762
|
+
return { ...signed, permissions: canonicalPermissions, resources: canonicalResources };
|
|
7763
|
+
}
|
|
7764
|
+
var RAW_ENCRYPTION_PREFIX = "urn:tinycloud:encryption:";
|
|
7765
|
+
var PKH_SPACE = /^tinycloud:pkh:eip155:(\d+):(0x[0-9a-fA-F]{40}):(.+)$/;
|
|
7766
|
+
function comparableSpace(space) {
|
|
7767
|
+
const match = PKH_SPACE.exec(space);
|
|
7768
|
+
return match ? `tinycloud:pkh:eip155:${match[1]}:${match[2].toLowerCase()}:${match[3]}` : space;
|
|
7769
|
+
}
|
|
7770
|
+
function signedAuthorityFromSessionGrant(node, delegation, authorization, cid) {
|
|
7771
|
+
const proof = delegation.siweProof;
|
|
7772
|
+
if (proof === void 0 || proof === null || typeof proof !== "object" || typeof proof.siwe !== "string" || typeof proof.signature !== "string") {
|
|
7773
|
+
reject(
|
|
7774
|
+
"proof_missing",
|
|
7775
|
+
"Runtime delegation is a wallet-signed session grant stored without its signed SIWE proof, so its authority cannot be verified."
|
|
7776
|
+
);
|
|
7777
|
+
}
|
|
7778
|
+
if (typeof node.verifySessionGrant !== "function") {
|
|
7779
|
+
reject("unsupported", "This runtime cannot verify wallet-signed session grants.");
|
|
7780
|
+
}
|
|
7781
|
+
const owner = `did:pkh:eip155:${delegation.chainId}:${delegation.ownerAddress}`;
|
|
7782
|
+
if (typeof node.did !== "string" || !didPrincipalsMatch(owner, node.did)) {
|
|
7783
|
+
reject("owner_mismatch", "Runtime delegation is not signed by this session's owner.");
|
|
7784
|
+
}
|
|
7785
|
+
let verified;
|
|
7786
|
+
try {
|
|
7787
|
+
verified = node.verifySessionGrant({
|
|
7788
|
+
delegationHeader: { Authorization: authorization },
|
|
7789
|
+
delegationCid: cid,
|
|
7790
|
+
spaceId: delegation.spaceId,
|
|
7791
|
+
address: delegation.ownerAddress,
|
|
7792
|
+
chainId: delegation.chainId,
|
|
7793
|
+
siwe: proof.siwe,
|
|
7794
|
+
signature: proof.signature
|
|
7795
|
+
});
|
|
7796
|
+
} catch (error) {
|
|
7797
|
+
reject("proof_invalid", error instanceof Error ? error.message : String(error));
|
|
7798
|
+
}
|
|
7799
|
+
const space = comparableSpace(delegation.spaceId);
|
|
7800
|
+
const spaceOwner = PKH_SPACE.exec(delegation.spaceId);
|
|
7801
|
+
if (!spaceOwner || !didPrincipalsMatch(`did:pkh:eip155:${spaceOwner[1]}:${spaceOwner[2]}`, owner)) {
|
|
7802
|
+
reject("authority_invalid", "Runtime delegation space is not owned by its signer.");
|
|
7803
|
+
}
|
|
7804
|
+
const groups = /* @__PURE__ */ new Map();
|
|
7805
|
+
for (const entry of verified.recap) {
|
|
7806
|
+
const service = entry.service.startsWith("tinycloud.") ? entry.service : `tinycloud.${entry.service}`;
|
|
7807
|
+
const raw = entry.space === "encryption";
|
|
7808
|
+
if (raw) {
|
|
7809
|
+
if (service !== "tinycloud.encryption" || !entry.path.startsWith(RAW_ENCRYPTION_PREFIX)) {
|
|
7810
|
+
reject("authority_invalid", "Runtime delegation has an unsupported signed raw resource.");
|
|
7811
|
+
}
|
|
7812
|
+
const networkOwner = entry.path.slice(RAW_ENCRYPTION_PREFIX.length, entry.path.lastIndexOf(":"));
|
|
7813
|
+
if (!didPrincipalsMatch(networkOwner, owner)) {
|
|
7814
|
+
reject("authority_invalid", "Runtime delegation names an encryption network its signer does not own.");
|
|
7815
|
+
}
|
|
7816
|
+
} else if (service === "tinycloud.encryption" || comparableSpace(entry.space) !== space) {
|
|
7817
|
+
reject("authority_invalid", "Runtime delegation has signed authority outside its space.");
|
|
7818
|
+
}
|
|
7819
|
+
const caveats = signedCaveats(entry.caveats ?? []);
|
|
7820
|
+
const key = JSON.stringify([service, raw ? "" : entry.space, entry.path, canonicalizeRecapCaveats2(caveats)]);
|
|
7821
|
+
const group = groups.get(key) ?? {
|
|
7822
|
+
service,
|
|
7823
|
+
...raw ? {} : { space: entry.space },
|
|
7824
|
+
path: entry.path,
|
|
7825
|
+
actions: [],
|
|
7826
|
+
...caveats === void 0 ? {} : { caveats }
|
|
7827
|
+
};
|
|
7828
|
+
for (const action of entry.actions) {
|
|
7829
|
+
if (!group.actions.includes(action)) group.actions.push(action);
|
|
7830
|
+
}
|
|
7831
|
+
groups.set(key, group);
|
|
7832
|
+
}
|
|
7833
|
+
return canonicalSignedAuthority({
|
|
7834
|
+
issuer: owner,
|
|
7835
|
+
// The verifier bound the SIWE audience to this runtime's own session key.
|
|
7836
|
+
audience: node.sessionDid,
|
|
7837
|
+
expiry: verified.expiresAt,
|
|
7838
|
+
proofs: []
|
|
7839
|
+
}, [...groups.values()]);
|
|
7642
7840
|
}
|
|
7643
7841
|
function canonicalResourcesFromPortableDelegation(delegation) {
|
|
7644
7842
|
if (delegation.resources !== void 0 && delegation.resources.length > 0) {
|
|
@@ -7674,46 +7872,58 @@ function resourcesMatch(actual, expected) {
|
|
|
7674
7872
|
async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
7675
7873
|
const host = normalizedHost(options.host);
|
|
7676
7874
|
if (delegation.host !== void 0 && normalizedHost(delegation.host) !== host) {
|
|
7677
|
-
|
|
7875
|
+
reject(
|
|
7876
|
+
"host_mismatch",
|
|
7678
7877
|
`Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`
|
|
7679
7878
|
);
|
|
7680
7879
|
}
|
|
7681
7880
|
if (!(delegation.expiry instanceof Date) || Number.isNaN(delegation.expiry.getTime())) {
|
|
7682
|
-
|
|
7881
|
+
reject("authority_invalid", "Runtime delegation has an invalid expiry.");
|
|
7683
7882
|
}
|
|
7684
7883
|
if (delegation.expiry.getTime() <= Date.now()) {
|
|
7685
|
-
|
|
7884
|
+
reject("expired", "Runtime delegation is expired.");
|
|
7686
7885
|
}
|
|
7687
7886
|
const authorization = delegation.delegationHeader?.Authorization;
|
|
7688
7887
|
if (typeof authorization !== "string" || authorization.length === 0) {
|
|
7689
|
-
|
|
7888
|
+
reject("authority_invalid", "Runtime delegation is missing authorization bytes.");
|
|
7690
7889
|
}
|
|
7691
7890
|
const cid = node.computeDelegationCid(authorization);
|
|
7692
7891
|
if (cid !== delegation.cid) {
|
|
7693
|
-
|
|
7892
|
+
reject("cid_mismatch", "Runtime delegation CID does not match authorization bytes.");
|
|
7893
|
+
}
|
|
7894
|
+
const compact = authorizationWithoutBearer(authorization).split(".").length === 3;
|
|
7895
|
+
let signed;
|
|
7896
|
+
try {
|
|
7897
|
+
signed = compact ? signedAuthorityFromCompactUcan(authorization) : signedAuthorityFromSessionGrant(node, delegation, authorization, cid);
|
|
7898
|
+
} catch (error) {
|
|
7899
|
+
if (error instanceof RuntimeDelegationRejectedError) throw error;
|
|
7900
|
+
reject("authority_invalid", error instanceof Error ? error.message : String(error));
|
|
7694
7901
|
}
|
|
7695
|
-
const signed = signedAuthorityFromCompactUcan(authorization);
|
|
7696
7902
|
if (signed.expiry.getTime() <= Date.now()) {
|
|
7697
|
-
|
|
7903
|
+
reject("expired", "Runtime delegation is expired.");
|
|
7698
7904
|
}
|
|
7699
7905
|
if (delegation.expiry.getTime() !== signed.expiry.getTime()) {
|
|
7700
|
-
|
|
7906
|
+
reject("authority_invalid", "Runtime delegation expiry does not match signed authority.");
|
|
7701
7907
|
}
|
|
7702
7908
|
if (!didPrincipalsMatch(signed.audience, node.sessionDid)) {
|
|
7703
|
-
|
|
7909
|
+
reject(
|
|
7910
|
+
"audience_mismatch",
|
|
7704
7911
|
`Runtime delegation targets ${signed.audience} but this session is ${node.sessionDid}.`
|
|
7705
7912
|
);
|
|
7706
7913
|
}
|
|
7707
7914
|
if (!didPrincipalsMatch(delegation.delegateDID, signed.audience)) {
|
|
7708
|
-
|
|
7915
|
+
reject("audience_mismatch", "Runtime delegation audience does not match signed authority.");
|
|
7916
|
+
}
|
|
7917
|
+
if (compact && !resourcesMatch(canonicalResourcesFromPortableDelegation(delegation), signed.resources)) {
|
|
7918
|
+
reject("resources_mismatch", "Runtime delegation resources do not match signed authority.");
|
|
7709
7919
|
}
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
throw new Error("Runtime delegation resources do not match signed authority.");
|
|
7920
|
+
if (options.authorize !== void 0 && !options.authorize(signed.permissions)) {
|
|
7921
|
+
throw new Error("Runtime delegation is not authorized for this runtime.");
|
|
7713
7922
|
}
|
|
7714
7923
|
const previouslyInstalled = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
7715
7924
|
if (previouslyInstalled !== void 0 && previouslyInstalled.delegationHeader.Authorization !== authorization) {
|
|
7716
|
-
|
|
7925
|
+
reject(
|
|
7926
|
+
"cid_mismatch",
|
|
7717
7927
|
"A different authorization is already installed for this runtime delegation CID."
|
|
7718
7928
|
);
|
|
7719
7929
|
}
|
|
@@ -7743,22 +7953,28 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
|
7743
7953
|
})),
|
|
7744
7954
|
expiry: new Date(signed.expiry),
|
|
7745
7955
|
delegateDID: signed.audience,
|
|
7746
|
-
host
|
|
7956
|
+
host,
|
|
7957
|
+
...compact ? {} : { siweProof: delegation.siweProof }
|
|
7747
7958
|
};
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7959
|
+
try {
|
|
7960
|
+
if (node.isSessionOnly === true && typeof node.did === "string" && principalDidEquals3(node.did, node.sessionDid) && typeof node.useDelegation === "function") {
|
|
7961
|
+
await node.useDelegation(installedCandidate);
|
|
7962
|
+
} else {
|
|
7963
|
+
await node.useRuntimeDelegation(installedCandidate);
|
|
7964
|
+
}
|
|
7965
|
+
} catch (error) {
|
|
7966
|
+
reject("activation_rejected", error instanceof Error ? error.message : String(error));
|
|
7752
7967
|
}
|
|
7753
7968
|
const installed = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
7754
7969
|
if (!installed) {
|
|
7755
|
-
|
|
7970
|
+
reject("activation_rejected", "Runtime delegation activation did not install the validated authority.");
|
|
7756
7971
|
}
|
|
7757
7972
|
if (installed.delegationHeader.Authorization !== authorization || !didPrincipalsMatch(installed.delegateDID, signed.audience) || installed.expiry.getTime() !== signed.expiry.getTime() || !resourcesMatch(
|
|
7758
7973
|
canonicalResourcesFromPortableDelegation(installed),
|
|
7759
7974
|
signed.resources
|
|
7760
7975
|
) || normalizedHost(installed.host ?? "") !== host) {
|
|
7761
|
-
|
|
7976
|
+
reject(
|
|
7977
|
+
"activation_rejected",
|
|
7762
7978
|
"Runtime delegation activation installed authority that differs from the validated authority."
|
|
7763
7979
|
);
|
|
7764
7980
|
}
|