@tinycloud/node-sdk 3.0.0-beta.9 → 3.0.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{core-BTuD37zN.d.cts → core-DufYfxi-.d.cts} +23 -8
- package/dist/{core-BTuD37zN.d.ts → core-DufYfxi-.d.ts} +23 -8
- package/dist/core.cjs +149 -101
- 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 +148 -101
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +151 -101
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +149 -101
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/core.cjs
CHANGED
|
@@ -50,6 +50,7 @@ __export(core_exports, {
|
|
|
50
50
|
DuckDbService: () => import_sdk_core15.DuckDbService,
|
|
51
51
|
FileSessionStorage: () => FileSessionStorage,
|
|
52
52
|
IdentityParseError: () => import_sdk_core11.IdentityParseError,
|
|
53
|
+
InvalidRestoredSessionError: () => InvalidRestoredSessionError,
|
|
53
54
|
KVService: () => import_sdk_core13.KVService,
|
|
54
55
|
ManifestValidationError: () => import_sdk_core12.ManifestValidationError,
|
|
55
56
|
MemorySessionStorage: () => MemorySessionStorage,
|
|
@@ -2285,6 +2286,22 @@ function cloneRecapCaveats(caveats) {
|
|
|
2285
2286
|
throw new Error("Verified persisted session ReCap contains invalid caveats.");
|
|
2286
2287
|
}
|
|
2287
2288
|
}
|
|
2289
|
+
function jsonFromVerifier(value) {
|
|
2290
|
+
if (value === void 0) return null;
|
|
2291
|
+
if (Array.isArray(value)) return value.map(jsonFromVerifier);
|
|
2292
|
+
if (value instanceof Map) {
|
|
2293
|
+
const entries = [...value.entries()];
|
|
2294
|
+
if (!entries.every(([key]) => typeof key === "string")) return value;
|
|
2295
|
+
return Object.fromEntries(entries.map(([key, nested]) => [key, jsonFromVerifier(nested)]));
|
|
2296
|
+
}
|
|
2297
|
+
if (value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
|
|
2298
|
+
return Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, jsonFromVerifier(nested)]));
|
|
2299
|
+
}
|
|
2300
|
+
return value;
|
|
2301
|
+
}
|
|
2302
|
+
function verifierRecapEntries(entries) {
|
|
2303
|
+
return entries.map((entry) => Array.isArray(entry.caveats) ? { ...entry, caveats: entry.caveats.map((caveat) => jsonFromVerifier(caveat)) } : entry);
|
|
2304
|
+
}
|
|
2288
2305
|
var ServiceGraphLifetime = class {
|
|
2289
2306
|
constructor(invokeFn, invokeAnyFn, fetchFn) {
|
|
2290
2307
|
this.invokeFn = invokeFn;
|
|
@@ -2357,18 +2374,23 @@ var UnsupportedSessionRestoreError = class extends Error {
|
|
|
2357
2374
|
this.name = "UnsupportedSessionRestoreError";
|
|
2358
2375
|
}
|
|
2359
2376
|
};
|
|
2377
|
+
var InvalidRestoredSessionError = class extends Error {
|
|
2378
|
+
constructor(message = "Persisted session authority is invalid or expired.") {
|
|
2379
|
+
super(message);
|
|
2380
|
+
this.code = "AUTH_EXPIRED";
|
|
2381
|
+
this.name = "InvalidRestoredSessionError";
|
|
2382
|
+
}
|
|
2383
|
+
};
|
|
2360
2384
|
function canonicalRestoredVerificationMethod(canonicalVerificationMethod, persistedVerificationMethod) {
|
|
2361
2385
|
if (typeof persistedVerificationMethod !== "string") return void 0;
|
|
2362
2386
|
const principal = canonicalVerificationMethod.split("#", 1)[0];
|
|
2363
2387
|
return persistedVerificationMethod === principal || persistedVerificationMethod === canonicalVerificationMethod ? canonicalVerificationMethod : void 0;
|
|
2364
2388
|
}
|
|
2365
2389
|
function persistedExpiry(value) {
|
|
2366
|
-
if (typeof value !== "string")
|
|
2367
|
-
throw new Error("Persisted session without a SIWE expiration must include expiresAt.");
|
|
2368
|
-
}
|
|
2390
|
+
if (typeof value !== "string") throw new InvalidRestoredSessionError("Persisted session must include expiresAt.");
|
|
2369
2391
|
const expiry = new Date(value);
|
|
2370
2392
|
if (!Number.isFinite(expiry.getTime()) || expiry.getTime() <= Date.now()) {
|
|
2371
|
-
throw new
|
|
2393
|
+
throw new InvalidRestoredSessionError();
|
|
2372
2394
|
}
|
|
2373
2395
|
return expiry;
|
|
2374
2396
|
}
|
|
@@ -2911,16 +2933,26 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2911
2933
|
*/
|
|
2912
2934
|
getVerifiedSessionCapabilities() {
|
|
2913
2935
|
const session = this.currentTinyCloudSession();
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
).map((entry) =>
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2936
|
+
return session?.siwe ? this.projectSignedRecapCapabilities(session.siwe) : [];
|
|
2937
|
+
}
|
|
2938
|
+
/** Normalize signed capabilities without collapsing nested encryption into raw authority. */
|
|
2939
|
+
projectSignedRecapCapabilities(siwe) {
|
|
2940
|
+
const projected = this.parseRecapWithCaveats(siwe).map((entry) => {
|
|
2941
|
+
const normalized = (0, import_sdk_core8.parseRecapCapabilities)(() => [entry], siwe)[0];
|
|
2942
|
+
if (normalized.service !== "tinycloud.encryption") return normalized;
|
|
2943
|
+
if (!this.isEncryptionNetworkOperation("encryption", entry.path, entry.space)) {
|
|
2944
|
+
return { ...normalized, space: entry.space };
|
|
2945
|
+
}
|
|
2946
|
+
return {
|
|
2947
|
+
service: normalized.service,
|
|
2948
|
+
path: normalized.path,
|
|
2949
|
+
actions: normalized.actions,
|
|
2950
|
+
...normalized.caveats === void 0 ? {} : { caveats: normalized.caveats }
|
|
2951
|
+
};
|
|
2952
|
+
});
|
|
2953
|
+
return projected.sort(
|
|
2954
|
+
(left, right) => (left.space ?? "").localeCompare(right.space ?? "") || left.service.localeCompare(right.service) || left.path.localeCompare(right.path)
|
|
2955
|
+
);
|
|
2924
2956
|
}
|
|
2925
2957
|
/**
|
|
2926
2958
|
* Get the Ethereum address for this user.
|
|
@@ -3375,9 +3407,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3375
3407
|
* We must keep the full owner-scoped URI so a synthetic primary grant can
|
|
3376
3408
|
* never cover an operation on a different owner's space.
|
|
3377
3409
|
*
|
|
3378
|
-
*
|
|
3379
|
-
*
|
|
3380
|
-
*
|
|
3410
|
+
* Only top-level encryption network entries (`space: "encryption"` or
|
|
3411
|
+
* absent) become `resource` ops. A network-shaped path nested under a
|
|
3412
|
+
* data space remains a `spaceId` op, not raw network authority.
|
|
3381
3413
|
* One op per action.
|
|
3382
3414
|
*
|
|
3383
3415
|
* Returns `[]` for session-only / restored-without-siwe modes and for any
|
|
@@ -3398,7 +3430,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3398
3430
|
operations = entries.flatMap((entry) => {
|
|
3399
3431
|
const service = this.invocationServiceName(entry.service);
|
|
3400
3432
|
return entry.actions.map((action) => ({
|
|
3401
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
|
|
3433
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId: entry.space },
|
|
3402
3434
|
service,
|
|
3403
3435
|
path: entry.path,
|
|
3404
3436
|
action,
|
|
@@ -3560,7 +3592,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3560
3592
|
return;
|
|
3561
3593
|
} catch (error) {
|
|
3562
3594
|
const message = error instanceof Error ? error.message : String(error);
|
|
3563
|
-
if (/Unauthorized Action|\
|
|
3595
|
+
if (/Unauthorized Action|\b(?:401|403)\b/.test(message)) {
|
|
3564
3596
|
console.warn(
|
|
3565
3597
|
"TinyCloud account registry sync stopped: authorization verdict is not retryable",
|
|
3566
3598
|
error
|
|
@@ -3810,9 +3842,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3810
3842
|
*/
|
|
3811
3843
|
async restoreSession(sessionData) {
|
|
3812
3844
|
await this.wasmBindings.ensureInitialized?.();
|
|
3813
|
-
|
|
3845
|
+
let restoredJwk;
|
|
3846
|
+
try {
|
|
3847
|
+
restoredJwk = clonePersistedSessionJwk(sessionData.jwk);
|
|
3848
|
+
} catch {
|
|
3849
|
+
throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
|
|
3850
|
+
}
|
|
3814
3851
|
if (sessionData.chainId !== void 0 && (!Number.isSafeInteger(sessionData.chainId) || sessionData.chainId <= 0)) {
|
|
3815
|
-
throw new
|
|
3852
|
+
throw new InvalidRestoredSessionError("Persisted session chain ID must be a positive safe integer.");
|
|
3816
3853
|
}
|
|
3817
3854
|
const stagedManager = this.wasmBindings.createSessionManager();
|
|
3818
3855
|
const stagedReplace = stagedManager.replaceSessionKey;
|
|
@@ -3824,32 +3861,31 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3824
3861
|
let stagedKeyId;
|
|
3825
3862
|
let stagedJwk;
|
|
3826
3863
|
let canonicalVerificationMethod;
|
|
3864
|
+
const keyIds = liveKeys.call(this.sessionManager);
|
|
3865
|
+
if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
|
|
3866
|
+
throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
|
|
3867
|
+
}
|
|
3868
|
+
for (const keyId of keyIds) {
|
|
3869
|
+
if (keyId === this.sessionKeyId) continue;
|
|
3870
|
+
const jwk = this.sessionManager.jwk(keyId);
|
|
3871
|
+
if (!jwk) throw new Error("missing live session key");
|
|
3872
|
+
stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
|
|
3873
|
+
}
|
|
3827
3874
|
try {
|
|
3828
|
-
const keyIds = liveKeys.call(this.sessionManager);
|
|
3829
|
-
if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
|
|
3830
|
-
throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
|
|
3831
|
-
}
|
|
3832
|
-
for (const keyId of keyIds) {
|
|
3833
|
-
if (keyId === this.sessionKeyId) continue;
|
|
3834
|
-
const jwk = this.sessionManager.jwk(keyId);
|
|
3835
|
-
if (!jwk) throw new Error("missing live session key");
|
|
3836
|
-
stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
|
|
3837
|
-
}
|
|
3838
3875
|
stagedKeyId = stagedReplace.call(stagedManager, restoredJwk, this.sessionKeyId);
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
|
|
3842
|
-
canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
|
|
3843
|
-
} catch (error) {
|
|
3844
|
-
if (error instanceof UnsupportedSessionRestoreError) throw error;
|
|
3845
|
-
throw new Error("Persisted session has an invalid private Ed25519 session key.");
|
|
3876
|
+
} catch {
|
|
3877
|
+
throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
|
|
3846
3878
|
}
|
|
3879
|
+
const stagedJwkJson = stagedManager.jwk(stagedKeyId);
|
|
3880
|
+
if (!stagedJwkJson) throw new Error("missing restored session key");
|
|
3881
|
+
stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
|
|
3882
|
+
canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
|
|
3847
3883
|
const restoredVerificationMethod = canonicalRestoredVerificationMethod(
|
|
3848
3884
|
canonicalVerificationMethod,
|
|
3849
3885
|
sessionData.verificationMethod
|
|
3850
3886
|
);
|
|
3851
3887
|
if (!restoredVerificationMethod) {
|
|
3852
|
-
throw new
|
|
3888
|
+
throw new InvalidRestoredSessionError(
|
|
3853
3889
|
"Persisted session verification method does not match its private Ed25519 session key."
|
|
3854
3890
|
);
|
|
3855
3891
|
}
|
|
@@ -3861,26 +3897,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3861
3897
|
];
|
|
3862
3898
|
const hasPersistedProof = proofValues.every((value) => value !== void 0);
|
|
3863
3899
|
if (!hasPersistedProof && (proofValues.some((value) => value !== void 0) || sessionData.expiresAt !== void 0)) {
|
|
3864
|
-
throw new
|
|
3900
|
+
throw new InvalidRestoredSessionError("Persisted session authority metadata is incomplete.");
|
|
3901
|
+
}
|
|
3902
|
+
let restoredAddress;
|
|
3903
|
+
try {
|
|
3904
|
+
restoredAddress = hasPersistedProof ? (0, import_sdk_core8.canonicalizeAddress)(sessionData.address) : void 0;
|
|
3905
|
+
} catch {
|
|
3906
|
+
throw new InvalidRestoredSessionError("Persisted session has an invalid wallet address.");
|
|
3865
3907
|
}
|
|
3866
|
-
const restoredAddress = hasPersistedProof ? (0, import_sdk_core8.canonicalizeAddress)(sessionData.address) : void 0;
|
|
3867
3908
|
let stagedSessionExpiry = /* @__PURE__ */ new Date(0);
|
|
3868
3909
|
let stagedRecap = [];
|
|
3869
3910
|
if (hasPersistedProof) {
|
|
3870
3911
|
if (typeof this.wasmBindings.validatePersistedSession !== "function") {
|
|
3871
3912
|
throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
|
|
3872
3913
|
}
|
|
3873
|
-
const verified =
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3914
|
+
const verified = (() => {
|
|
3915
|
+
try {
|
|
3916
|
+
return this.wasmBindings.validatePersistedSession({
|
|
3917
|
+
delegationHeader: sessionData.delegationHeader,
|
|
3918
|
+
delegationCid: sessionData.delegationCid,
|
|
3919
|
+
spaceId: sessionData.spaceId,
|
|
3920
|
+
jwk: stagedJwk,
|
|
3921
|
+
address: restoredAddress,
|
|
3922
|
+
chainId: sessionData.chainId,
|
|
3923
|
+
siwe: sessionData.siwe,
|
|
3924
|
+
signature: sessionData.signature
|
|
3925
|
+
});
|
|
3926
|
+
} catch (error) {
|
|
3927
|
+
throw new InvalidRestoredSessionError(error instanceof Error ? error.message : String(error));
|
|
3928
|
+
}
|
|
3929
|
+
})();
|
|
3930
|
+
const exactRecap = Array.isArray(verified.verifiedRecap) ? verifierRecapEntries(verified.verifiedRecap) : verified.verifiedRecap;
|
|
3884
3931
|
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
3885
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(
|
|
3886
3933
|
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
@@ -3900,7 +3947,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3900
3947
|
const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
|
|
3901
3948
|
const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
|
|
3902
3949
|
if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
|
|
3903
|
-
throw new
|
|
3950
|
+
throw new InvalidRestoredSessionError("Persisted session expiry does not match its signed SIWE authority.");
|
|
3904
3951
|
}
|
|
3905
3952
|
stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
|
|
3906
3953
|
}
|
|
@@ -3997,7 +4044,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3997
4044
|
operations = recap.flatMap((entry) => {
|
|
3998
4045
|
const service = this.invocationServiceName(entry.service);
|
|
3999
4046
|
return entry.actions.map((action) => ({
|
|
4000
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
|
|
4047
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId: entry.space },
|
|
4001
4048
|
service,
|
|
4002
4049
|
path: entry.path,
|
|
4003
4050
|
action,
|
|
@@ -4093,7 +4140,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4093
4140
|
const delegations = input.recap.map((entry) => ({
|
|
4094
4141
|
cid: session.delegationCid,
|
|
4095
4142
|
delegateDID: session.verificationMethod,
|
|
4096
|
-
spaceId: entry.space,
|
|
4143
|
+
spaceId: this.spaceIdsEqual(entry.space, session.spaceId) ? session.spaceId : entry.space,
|
|
4097
4144
|
path: entry.path,
|
|
4098
4145
|
actions: [...entry.actions],
|
|
4099
4146
|
caveats: cloneRecapCaveats(entry.caveats),
|
|
@@ -4783,16 +4830,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4783
4830
|
);
|
|
4784
4831
|
graph.assertActive();
|
|
4785
4832
|
if (!response.ok) {
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
const nested = typeof record?.error === "object" && record.error !== null ? record.error : void 0;
|
|
4792
|
-
permissionHint = (0, import_sdk_services.parsePermissionHint)(record?.permissionHint) ?? (0, import_sdk_services.parsePermissionHint)(nested?.permissionHint);
|
|
4793
|
-
} catch {
|
|
4794
|
-
}
|
|
4795
|
-
}
|
|
4833
|
+
const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services.parsePermissionHint)({
|
|
4834
|
+
service: "tinycloud.encryption",
|
|
4835
|
+
path: networkId,
|
|
4836
|
+
actions: ["tinycloud.encryption/decrypt"]
|
|
4837
|
+
}) : void 0;
|
|
4796
4838
|
throw new import_sdk_core8.DecryptTransportResponseError(response.status, permissionHint);
|
|
4797
4839
|
}
|
|
4798
4840
|
return await response.json();
|
|
@@ -5231,13 +5273,20 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5231
5273
|
const session = this.currentTinyCloudSession();
|
|
5232
5274
|
const serviceSession = this._serviceContext?.session;
|
|
5233
5275
|
if (!session || !serviceSession) throw new Error("Share delivery requires an authenticated session");
|
|
5276
|
+
const idempotencyBytes = new TextEncoder().encode(input.idempotencyKey);
|
|
5277
|
+
if (idempotencyBytes.byteLength === 0 || idempotencyBytes.byteLength > 512) {
|
|
5278
|
+
throw new Error("Share delivery idempotency key is invalid");
|
|
5279
|
+
}
|
|
5280
|
+
const jtiDigest = new Uint8Array(await crypto.subtle.digest("SHA-256", idempotencyBytes));
|
|
5234
5281
|
const body = {
|
|
5235
5282
|
envelope: input.envelope,
|
|
5283
|
+
sealedEnvelope: input.sealedEnvelope,
|
|
5284
|
+
envelopeKey: input.envelopeKey,
|
|
5236
5285
|
shareCid: input.shareCid,
|
|
5237
5286
|
recipientEmail: input.recipientEmail,
|
|
5238
5287
|
shareUrl: input.shareUrl,
|
|
5239
5288
|
documentName: input.documentName,
|
|
5240
|
-
jti: base64UrlEncode(
|
|
5289
|
+
jti: base64UrlEncode(jtiDigest.slice(0, 16)),
|
|
5241
5290
|
expiresAt: new Date(input.expiresAt).toISOString().replace(/\.\d{3}Z$/, "Z")
|
|
5242
5291
|
};
|
|
5243
5292
|
const requestBodyDigest = base64UrlEncode(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode((0, import_sdk_core8.canonicalizeEncryptionJson)(body)))));
|
|
@@ -5720,7 +5769,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5720
5769
|
continue;
|
|
5721
5770
|
}
|
|
5722
5771
|
let space;
|
|
5723
|
-
if (service !== "tinycloud.encryption") {
|
|
5772
|
+
if (service !== "tinycloud.encryption" || operation.resource === void 0) {
|
|
5724
5773
|
if (typeof operation.spaceId !== "string") continue;
|
|
5725
5774
|
try {
|
|
5726
5775
|
space = this.resolvePermissionSpace(operation.spaceId, session);
|
|
@@ -6265,11 +6314,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6265
6314
|
);
|
|
6266
6315
|
return { delegation: delegation2, prompted: true };
|
|
6267
6316
|
}
|
|
6268
|
-
const granted = (
|
|
6269
|
-
|
|
6270
|
-
session.siwe
|
|
6271
|
-
);
|
|
6272
|
-
const { subset, missing } = (0, import_sdk_core8.isCapabilitySubset)(expandedEntries, granted);
|
|
6317
|
+
const granted = this.projectSignedRecapCapabilities(session.siwe);
|
|
6318
|
+
const { subset, missing } = this.signedCapabilitySubset(expandedEntries, granted);
|
|
6273
6319
|
if (!subset) {
|
|
6274
6320
|
const runtimeOperations = this.permissionEntriesToOperations(expandedEntries, session);
|
|
6275
6321
|
const runtimeGrant = this.findGrantForOperations(
|
|
@@ -6589,7 +6635,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6589
6635
|
return abilities;
|
|
6590
6636
|
}
|
|
6591
6637
|
isEncryptionPermissionEntry(entry) {
|
|
6592
|
-
return entry.service === import_sdk_core8.ENCRYPTION_PERMISSION_SERVICE &&
|
|
6638
|
+
return entry.service === import_sdk_core8.ENCRYPTION_PERMISSION_SERVICE && this.isEncryptionNetworkOperation("encryption", entry.path, entry.space);
|
|
6593
6639
|
}
|
|
6594
6640
|
permissionsToRawAbilities(entries) {
|
|
6595
6641
|
const rawAbilities = {};
|
|
@@ -6613,7 +6659,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6613
6659
|
return entries.flatMap((entry) => {
|
|
6614
6660
|
const service = this.shortServiceName(entry.service);
|
|
6615
6661
|
return entry.actions.map((action) => ({
|
|
6616
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
6662
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
|
|
6617
6663
|
service,
|
|
6618
6664
|
path: entry.path,
|
|
6619
6665
|
action,
|
|
@@ -6621,13 +6667,21 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6621
6667
|
}));
|
|
6622
6668
|
});
|
|
6623
6669
|
}
|
|
6670
|
+
/** Raw network authority cannot be covered by a nested encryption space. */
|
|
6671
|
+
signedCapabilitySubset(requested, granted) {
|
|
6672
|
+
const rawGranted = granted.filter((entry) => this.isEncryptionPermissionEntry(entry)).map((entry) => ({ ...entry, space: "encryption" }));
|
|
6673
|
+
const spaceGranted = granted.filter((entry) => !this.isEncryptionPermissionEntry(entry));
|
|
6674
|
+
const missing = requested.filter((entry) => {
|
|
6675
|
+
const raw = this.isEncryptionPermissionEntry(entry);
|
|
6676
|
+
const comparable = raw ? { ...entry, space: "encryption" } : entry;
|
|
6677
|
+
return !(0, import_sdk_core8.isCapabilitySubset)([comparable], raw ? rawGranted : spaceGranted).subset;
|
|
6678
|
+
});
|
|
6679
|
+
return { subset: missing.length === 0, missing };
|
|
6680
|
+
}
|
|
6624
6681
|
sessionCoversPermissionEntries(session, entries) {
|
|
6625
6682
|
try {
|
|
6626
|
-
const granted = (
|
|
6627
|
-
|
|
6628
|
-
session.siwe
|
|
6629
|
-
);
|
|
6630
|
-
return (0, import_sdk_core8.isCapabilitySubset)(entries, granted).subset;
|
|
6683
|
+
const granted = this.projectSignedRecapCapabilities(session.siwe);
|
|
6684
|
+
return this.signedCapabilitySubset(entries, granted).subset;
|
|
6631
6685
|
} catch {
|
|
6632
6686
|
return false;
|
|
6633
6687
|
}
|
|
@@ -6637,7 +6691,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6637
6691
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
6638
6692
|
const service = this.shortServiceName(entry.service);
|
|
6639
6693
|
return entry.actions.map((action) => ({
|
|
6640
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
6694
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
|
|
6641
6695
|
service,
|
|
6642
6696
|
path: entry.path,
|
|
6643
6697
|
action,
|
|
@@ -6726,7 +6780,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6726
6780
|
return resources.flatMap((resource) => {
|
|
6727
6781
|
const service = this.invocationServiceName(resource.service);
|
|
6728
6782
|
return resource.actions.map((action) => ({
|
|
6729
|
-
...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
|
|
6783
|
+
...this.isEncryptionNetworkOperation(service, resource.path, resource.space) ? { resource: resource.path } : { spaceId: resource.space },
|
|
6730
6784
|
service,
|
|
6731
6785
|
path: resource.path,
|
|
6732
6786
|
action,
|
|
@@ -6787,7 +6841,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6787
6841
|
};
|
|
6788
6842
|
for (const resource of resources) {
|
|
6789
6843
|
const service = this.invocationServiceName(resource.service);
|
|
6790
|
-
if (this.isEncryptionNetworkOperation(service, resource.path)) {
|
|
6844
|
+
if (this.isEncryptionNetworkOperation(service, resource.path, resource.space)) {
|
|
6791
6845
|
rawAbilities[_a = resource.path] ?? (rawAbilities[_a] = []);
|
|
6792
6846
|
addActions(rawAbilities[resource.path], resource.actions);
|
|
6793
6847
|
continue;
|
|
@@ -6856,21 +6910,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6856
6910
|
}
|
|
6857
6911
|
return granted.spaceId !== void 0 && requested.spaceId !== void 0 && this.spaceIdsEqual(granted.spaceId, requested.spaceId) && this.pathContains(granted.path, requested.path);
|
|
6858
6912
|
}
|
|
6859
|
-
// Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`.
|
|
6860
|
-
// EIP-155 address
|
|
6861
|
-
//
|
|
6862
|
-
// the EIP-55 checksummed form — so a byte-for-byte compare spuriously rejects
|
|
6863
|
-
// an otherwise-valid grant. Lowercase ONLY the `eip155:<chain>:0x<addr>`
|
|
6864
|
-
// segment and leave everything else (crucially the case-sensitive space NAME)
|
|
6865
|
-
// byte-exact. Mirrors the CLI's `normalizeSpaceForCompare` (OPENKEY_SCOPE_MISMATCH fix).
|
|
6913
|
+
// Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`. Compare
|
|
6914
|
+
// only the embedded EIP-155 address without case sensitivity; chain, name,
|
|
6915
|
+
// and all non-PKH space identifiers remain byte-exact.
|
|
6866
6916
|
spaceIdsEqual(a, b) {
|
|
6867
6917
|
return this.normalizeSpaceAddress(a) === this.normalizeSpaceAddress(b);
|
|
6868
6918
|
}
|
|
6869
6919
|
normalizeSpaceAddress(space) {
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
(_match, prefix, addr) => prefix + addr.toLowerCase()
|
|
6873
|
-
);
|
|
6920
|
+
const match = /^(tinycloud:pkh:eip155:\d+:)(0x[0-9a-fA-F]{40})(:.+)$/.exec(space);
|
|
6921
|
+
return match === null ? space : `${match[1]}${match[2].toLowerCase()}${match[3]}`;
|
|
6874
6922
|
}
|
|
6875
6923
|
actionContains(grantedAction, requestedAction) {
|
|
6876
6924
|
if (grantedAction === requestedAction) {
|
|
@@ -6887,10 +6935,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6887
6935
|
}
|
|
6888
6936
|
/** Prefer the v2 caveat-preserving parser while retaining old custom WASM. */
|
|
6889
6937
|
parseRecapWithCaveats(siwe) {
|
|
6890
|
-
|
|
6938
|
+
const verified = this.wasmBindings.parseVerifiedRecapFromSiwe?.(siwe);
|
|
6939
|
+
return verified == null ? this.wasmBindings.parseRecapFromSiwe(siwe) : verifierRecapEntries(verified);
|
|
6891
6940
|
}
|
|
6892
|
-
isEncryptionNetworkOperation(service, path) {
|
|
6893
|
-
return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
|
|
6941
|
+
isEncryptionNetworkOperation(service, path, space) {
|
|
6942
|
+
return service === "encryption" && (space === void 0 || space === "encryption") && path.startsWith("urn:tinycloud:encryption:");
|
|
6894
6943
|
}
|
|
6895
6944
|
operationFromInvokeAnyEntry(entry) {
|
|
6896
6945
|
const service = this.invocationServiceName(entry.service);
|
|
@@ -6903,7 +6952,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6903
6952
|
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
6904
6953
|
};
|
|
6905
6954
|
}
|
|
6906
|
-
if (this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
6955
|
+
if (entry.spaceId === void 0 && this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
6907
6956
|
return {
|
|
6908
6957
|
resource: entry.path,
|
|
6909
6958
|
service,
|
|
@@ -7188,8 +7237,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7188
7237
|
const jwk = mySession.jwk;
|
|
7189
7238
|
const { abilities, rawAbilities } = this.buildActivationAbilities(delegation);
|
|
7190
7239
|
const now = /* @__PURE__ */ new Date();
|
|
7191
|
-
const
|
|
7192
|
-
const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
|
|
7240
|
+
const expirationTime = delegation.expiry;
|
|
7193
7241
|
const prepared = this.wasmBindings.prepareSession({
|
|
7194
7242
|
abilities,
|
|
7195
7243
|
address: this.wasmBindings.ensureEip55(mySession.address),
|
|
@@ -7293,8 +7341,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7293
7341
|
);
|
|
7294
7342
|
}
|
|
7295
7343
|
const now = /* @__PURE__ */ new Date();
|
|
7296
|
-
const
|
|
7297
|
-
const requestedExpiry = new Date(now.getTime() + expiryMs);
|
|
7344
|
+
const requestedExpiry = params.expiryMs === void 0 ? parentDelegation.expiry : new Date(now.getTime() + params.expiryMs);
|
|
7298
7345
|
const actualExpiry = requestedExpiry > parentDelegation.expiry ? parentDelegation.expiry : requestedExpiry;
|
|
7299
7346
|
const abilities = {};
|
|
7300
7347
|
const rawAbilities = {};
|
|
@@ -7750,6 +7797,7 @@ var import_sdk_core21 = require("@tinycloud/sdk-core");
|
|
|
7750
7797
|
DuckDbService,
|
|
7751
7798
|
FileSessionStorage,
|
|
7752
7799
|
IdentityParseError,
|
|
7800
|
+
InvalidRestoredSessionError,
|
|
7753
7801
|
KVService,
|
|
7754
7802
|
ManifestValidationError,
|
|
7755
7803
|
MemorySessionStorage,
|