@tinycloud/node-sdk 3.0.0-beta.2 → 3.0.0-beta.20
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-C6Czyr52.d.cts → core-DufYfxi-.d.cts} +39 -61
- package/dist/{core-C6Czyr52.d.ts → core-DufYfxi-.d.ts} +39 -61
- package/dist/core.cjs +213 -139
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +216 -141
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +215 -139
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -3
- package/dist/index.d.ts +25 -3
- package/dist/index.js +217 -141
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -17209,11 +17209,13 @@ import {
|
|
|
17209
17209
|
canonicalizeAddress as canonicalizeAddress2,
|
|
17210
17210
|
pkhDid as pkhDid2,
|
|
17211
17211
|
resolveTinyCloudHosts as resolveTinyCloudHosts2,
|
|
17212
|
+
publishLocationRecord,
|
|
17212
17213
|
principalDidEquals as principalDidEquals2,
|
|
17213
17214
|
parseNetworkId as parseNetworkId2,
|
|
17214
17215
|
resolveSecretPath as resolveSecretPath2,
|
|
17215
|
-
|
|
17216
|
-
|
|
17216
|
+
registerPolicyV3,
|
|
17217
|
+
CREDENTIAL_INVITATION_REQUEST_DOMAIN,
|
|
17218
|
+
validateShareDeliveryAuthorizationV3Bytes,
|
|
17217
17219
|
verifyEip191MessageSignature,
|
|
17218
17220
|
signCompactUcanRootAuthorization
|
|
17219
17221
|
} from "@tinycloud/sdk-core";
|
|
@@ -19094,6 +19096,11 @@ var NodeSecretsService = class {
|
|
|
19094
19096
|
if (!permission.ok) return permission;
|
|
19095
19097
|
return options === void 0 ? this.service.list() : this.service.list(options);
|
|
19096
19098
|
}
|
|
19099
|
+
async listAll() {
|
|
19100
|
+
const permission = await this.ensurePermission("", void 0, "list");
|
|
19101
|
+
if (!permission.ok) return permission;
|
|
19102
|
+
return this.service.listAll();
|
|
19103
|
+
}
|
|
19097
19104
|
get service() {
|
|
19098
19105
|
return this.config.getService();
|
|
19099
19106
|
}
|
|
@@ -19155,7 +19162,11 @@ var NodeSecretsService = class {
|
|
|
19155
19162
|
(entry) => manifests.some((candidate) => {
|
|
19156
19163
|
const resolved = resolveManifest(candidate);
|
|
19157
19164
|
return resolved.resources.some(
|
|
19158
|
-
(resource) => resource.service === entry.service && spaceMatches(
|
|
19165
|
+
(resource) => resource.service === entry.service && spaceMatches(
|
|
19166
|
+
resource.space,
|
|
19167
|
+
entry.space,
|
|
19168
|
+
this.config.resolveSpace
|
|
19169
|
+
) && resource.path === entry.path && entry.actions.every((action) => resource.actions.includes(action))
|
|
19159
19170
|
);
|
|
19160
19171
|
})
|
|
19161
19172
|
);
|
|
@@ -19288,6 +19299,22 @@ function cloneRecapCaveats(caveats) {
|
|
|
19288
19299
|
throw new Error("Verified persisted session ReCap contains invalid caveats.");
|
|
19289
19300
|
}
|
|
19290
19301
|
}
|
|
19302
|
+
function jsonFromVerifier(value) {
|
|
19303
|
+
if (value === void 0) return null;
|
|
19304
|
+
if (Array.isArray(value)) return value.map(jsonFromVerifier);
|
|
19305
|
+
if (value instanceof Map) {
|
|
19306
|
+
const entries = [...value.entries()];
|
|
19307
|
+
if (!entries.every(([key2]) => typeof key2 === "string")) return value;
|
|
19308
|
+
return Object.fromEntries(entries.map(([key2, nested]) => [key2, jsonFromVerifier(nested)]));
|
|
19309
|
+
}
|
|
19310
|
+
if (value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
|
|
19311
|
+
return Object.fromEntries(Object.entries(value).map(([key2, nested]) => [key2, jsonFromVerifier(nested)]));
|
|
19312
|
+
}
|
|
19313
|
+
return value;
|
|
19314
|
+
}
|
|
19315
|
+
function verifierRecapEntries(entries) {
|
|
19316
|
+
return entries.map((entry) => Array.isArray(entry.caveats) ? { ...entry, caveats: entry.caveats.map((caveat) => jsonFromVerifier(caveat)) } : entry);
|
|
19317
|
+
}
|
|
19291
19318
|
var ServiceGraphLifetime = class {
|
|
19292
19319
|
constructor(invokeFn, invokeAnyFn, fetchFn) {
|
|
19293
19320
|
this.invokeFn = invokeFn;
|
|
@@ -19360,18 +19387,23 @@ var UnsupportedSessionRestoreError = class extends Error {
|
|
|
19360
19387
|
this.name = "UnsupportedSessionRestoreError";
|
|
19361
19388
|
}
|
|
19362
19389
|
};
|
|
19390
|
+
var InvalidRestoredSessionError = class extends Error {
|
|
19391
|
+
constructor(message = "Persisted session authority is invalid or expired.") {
|
|
19392
|
+
super(message);
|
|
19393
|
+
this.code = "AUTH_EXPIRED";
|
|
19394
|
+
this.name = "InvalidRestoredSessionError";
|
|
19395
|
+
}
|
|
19396
|
+
};
|
|
19363
19397
|
function canonicalRestoredVerificationMethod(canonicalVerificationMethod, persistedVerificationMethod) {
|
|
19364
19398
|
if (typeof persistedVerificationMethod !== "string") return void 0;
|
|
19365
19399
|
const principal = canonicalVerificationMethod.split("#", 1)[0];
|
|
19366
19400
|
return persistedVerificationMethod === principal || persistedVerificationMethod === canonicalVerificationMethod ? canonicalVerificationMethod : void 0;
|
|
19367
19401
|
}
|
|
19368
19402
|
function persistedExpiry(value) {
|
|
19369
|
-
if (typeof value !== "string")
|
|
19370
|
-
throw new Error("Persisted session without a SIWE expiration must include expiresAt.");
|
|
19371
|
-
}
|
|
19403
|
+
if (typeof value !== "string") throw new InvalidRestoredSessionError("Persisted session must include expiresAt.");
|
|
19372
19404
|
const expiry = new Date(value);
|
|
19373
19405
|
if (!Number.isFinite(expiry.getTime()) || expiry.getTime() <= Date.now()) {
|
|
19374
|
-
throw new
|
|
19406
|
+
throw new InvalidRestoredSessionError();
|
|
19375
19407
|
}
|
|
19376
19408
|
return expiry;
|
|
19377
19409
|
}
|
|
@@ -19850,6 +19882,29 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19850
19882
|
const authHosts = this.auth?.hosts ?? [];
|
|
19851
19883
|
return authHosts.length > 0 ? authHosts : [this.config.host];
|
|
19852
19884
|
}
|
|
19885
|
+
/** The registry-resolved Node endpoint and the identity returned by that exact Node. */
|
|
19886
|
+
async activeNodeIdentity() {
|
|
19887
|
+
const host = this.hosts[0];
|
|
19888
|
+
if (host === void 0) throw new Error("TinyCloud node host has not been resolved");
|
|
19889
|
+
const parsed = new URL(host);
|
|
19890
|
+
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && (parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost")) || parsed.origin !== host) {
|
|
19891
|
+
throw new Error("TinyCloud node host is not a canonical origin");
|
|
19892
|
+
}
|
|
19893
|
+
const nodeDid = await this.fetchNodeId();
|
|
19894
|
+
if (!nodeDid.startsWith("did:key:z")) throw new Error("TinyCloud node identity is not a did:key");
|
|
19895
|
+
return Object.freeze({ origin: parsed.origin, nodeDid });
|
|
19896
|
+
}
|
|
19897
|
+
/** Publish the active owner Node as a session-signed registry record. */
|
|
19898
|
+
async publishActiveNodeLocation(registryUrl, fetchFn = globalThis.fetch.bind(globalThis)) {
|
|
19899
|
+
const node = await this.activeNodeIdentity();
|
|
19900
|
+
return publishLocationRecord({
|
|
19901
|
+
registryUrl,
|
|
19902
|
+
subject: this.credentialHolderDid,
|
|
19903
|
+
nodeOrigin: node.origin,
|
|
19904
|
+
signer: { type: "did:key", signBytes: (bytes) => this.signSessionBytes(bytes) },
|
|
19905
|
+
fetch: fetchFn
|
|
19906
|
+
});
|
|
19907
|
+
}
|
|
19853
19908
|
/**
|
|
19854
19909
|
* Get the primary identity DID for this user.
|
|
19855
19910
|
* - If wallet connected and signed in: returns PKH DID (did:pkh:eip155:{chainId}:{address})
|
|
@@ -19891,16 +19946,26 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19891
19946
|
*/
|
|
19892
19947
|
getVerifiedSessionCapabilities() {
|
|
19893
19948
|
const session = this.currentTinyCloudSession();
|
|
19894
|
-
|
|
19895
|
-
|
|
19896
|
-
|
|
19897
|
-
|
|
19898
|
-
).map((entry) =>
|
|
19899
|
-
|
|
19900
|
-
|
|
19901
|
-
|
|
19902
|
-
|
|
19903
|
-
|
|
19949
|
+
return session?.siwe ? this.projectSignedRecapCapabilities(session.siwe) : [];
|
|
19950
|
+
}
|
|
19951
|
+
/** Normalize signed capabilities without collapsing nested encryption into raw authority. */
|
|
19952
|
+
projectSignedRecapCapabilities(siwe) {
|
|
19953
|
+
const projected = this.parseRecapWithCaveats(siwe).map((entry) => {
|
|
19954
|
+
const normalized = parseRecapCapabilities(() => [entry], siwe)[0];
|
|
19955
|
+
if (normalized.service !== "tinycloud.encryption") return normalized;
|
|
19956
|
+
if (!this.isEncryptionNetworkOperation("encryption", entry.path, entry.space)) {
|
|
19957
|
+
return { ...normalized, space: entry.space };
|
|
19958
|
+
}
|
|
19959
|
+
return {
|
|
19960
|
+
service: normalized.service,
|
|
19961
|
+
path: normalized.path,
|
|
19962
|
+
actions: normalized.actions,
|
|
19963
|
+
...normalized.caveats === void 0 ? {} : { caveats: normalized.caveats }
|
|
19964
|
+
};
|
|
19965
|
+
});
|
|
19966
|
+
return projected.sort(
|
|
19967
|
+
(left, right) => (left.space ?? "").localeCompare(right.space ?? "") || left.service.localeCompare(right.service) || left.path.localeCompare(right.path)
|
|
19968
|
+
);
|
|
19904
19969
|
}
|
|
19905
19970
|
/**
|
|
19906
19971
|
* Get the Ethereum address for this user.
|
|
@@ -20355,9 +20420,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20355
20420
|
* We must keep the full owner-scoped URI so a synthetic primary grant can
|
|
20356
20421
|
* never cover an operation on a different owner's space.
|
|
20357
20422
|
*
|
|
20358
|
-
*
|
|
20359
|
-
*
|
|
20360
|
-
*
|
|
20423
|
+
* Only top-level encryption network entries (`space: "encryption"` or
|
|
20424
|
+
* absent) become `resource` ops. A network-shaped path nested under a
|
|
20425
|
+
* data space remains a `spaceId` op, not raw network authority.
|
|
20361
20426
|
* One op per action.
|
|
20362
20427
|
*
|
|
20363
20428
|
* Returns `[]` for session-only / restored-without-siwe modes and for any
|
|
@@ -20378,7 +20443,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20378
20443
|
operations = entries.flatMap((entry) => {
|
|
20379
20444
|
const service = this.invocationServiceName(entry.service);
|
|
20380
20445
|
return entry.actions.map((action) => ({
|
|
20381
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
|
|
20446
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId: entry.space },
|
|
20382
20447
|
service,
|
|
20383
20448
|
path: entry.path,
|
|
20384
20449
|
action,
|
|
@@ -20540,7 +20605,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20540
20605
|
return;
|
|
20541
20606
|
} catch (error) {
|
|
20542
20607
|
const message = error instanceof Error ? error.message : String(error);
|
|
20543
|
-
if (/Unauthorized Action|\
|
|
20608
|
+
if (/Unauthorized Action|\b(?:401|403)\b/.test(message)) {
|
|
20544
20609
|
console.warn(
|
|
20545
20610
|
"TinyCloud account registry sync stopped: authorization verdict is not retryable",
|
|
20546
20611
|
error
|
|
@@ -20790,9 +20855,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20790
20855
|
*/
|
|
20791
20856
|
async restoreSession(sessionData) {
|
|
20792
20857
|
await this.wasmBindings.ensureInitialized?.();
|
|
20793
|
-
|
|
20858
|
+
let restoredJwk;
|
|
20859
|
+
try {
|
|
20860
|
+
restoredJwk = clonePersistedSessionJwk(sessionData.jwk);
|
|
20861
|
+
} catch {
|
|
20862
|
+
throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
|
|
20863
|
+
}
|
|
20794
20864
|
if (sessionData.chainId !== void 0 && (!Number.isSafeInteger(sessionData.chainId) || sessionData.chainId <= 0)) {
|
|
20795
|
-
throw new
|
|
20865
|
+
throw new InvalidRestoredSessionError("Persisted session chain ID must be a positive safe integer.");
|
|
20796
20866
|
}
|
|
20797
20867
|
const stagedManager = this.wasmBindings.createSessionManager();
|
|
20798
20868
|
const stagedReplace = stagedManager.replaceSessionKey;
|
|
@@ -20804,32 +20874,31 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20804
20874
|
let stagedKeyId;
|
|
20805
20875
|
let stagedJwk;
|
|
20806
20876
|
let canonicalVerificationMethod;
|
|
20877
|
+
const keyIds = liveKeys.call(this.sessionManager);
|
|
20878
|
+
if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
|
|
20879
|
+
throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
|
|
20880
|
+
}
|
|
20881
|
+
for (const keyId of keyIds) {
|
|
20882
|
+
if (keyId === this.sessionKeyId) continue;
|
|
20883
|
+
const jwk = this.sessionManager.jwk(keyId);
|
|
20884
|
+
if (!jwk) throw new Error("missing live session key");
|
|
20885
|
+
stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
|
|
20886
|
+
}
|
|
20807
20887
|
try {
|
|
20808
|
-
const keyIds = liveKeys.call(this.sessionManager);
|
|
20809
|
-
if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
|
|
20810
|
-
throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
|
|
20811
|
-
}
|
|
20812
|
-
for (const keyId of keyIds) {
|
|
20813
|
-
if (keyId === this.sessionKeyId) continue;
|
|
20814
|
-
const jwk = this.sessionManager.jwk(keyId);
|
|
20815
|
-
if (!jwk) throw new Error("missing live session key");
|
|
20816
|
-
stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
|
|
20817
|
-
}
|
|
20818
20888
|
stagedKeyId = stagedReplace.call(stagedManager, restoredJwk, this.sessionKeyId);
|
|
20819
|
-
|
|
20820
|
-
|
|
20821
|
-
stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
|
|
20822
|
-
canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
|
|
20823
|
-
} catch (error) {
|
|
20824
|
-
if (error instanceof UnsupportedSessionRestoreError) throw error;
|
|
20825
|
-
throw new Error("Persisted session has an invalid private Ed25519 session key.");
|
|
20889
|
+
} catch {
|
|
20890
|
+
throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
|
|
20826
20891
|
}
|
|
20892
|
+
const stagedJwkJson = stagedManager.jwk(stagedKeyId);
|
|
20893
|
+
if (!stagedJwkJson) throw new Error("missing restored session key");
|
|
20894
|
+
stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
|
|
20895
|
+
canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
|
|
20827
20896
|
const restoredVerificationMethod = canonicalRestoredVerificationMethod(
|
|
20828
20897
|
canonicalVerificationMethod,
|
|
20829
20898
|
sessionData.verificationMethod
|
|
20830
20899
|
);
|
|
20831
20900
|
if (!restoredVerificationMethod) {
|
|
20832
|
-
throw new
|
|
20901
|
+
throw new InvalidRestoredSessionError(
|
|
20833
20902
|
"Persisted session verification method does not match its private Ed25519 session key."
|
|
20834
20903
|
);
|
|
20835
20904
|
}
|
|
@@ -20841,26 +20910,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20841
20910
|
];
|
|
20842
20911
|
const hasPersistedProof = proofValues.every((value) => value !== void 0);
|
|
20843
20912
|
if (!hasPersistedProof && (proofValues.some((value) => value !== void 0) || sessionData.expiresAt !== void 0)) {
|
|
20844
|
-
throw new
|
|
20913
|
+
throw new InvalidRestoredSessionError("Persisted session authority metadata is incomplete.");
|
|
20914
|
+
}
|
|
20915
|
+
let restoredAddress;
|
|
20916
|
+
try {
|
|
20917
|
+
restoredAddress = hasPersistedProof ? canonicalizeAddress2(sessionData.address) : void 0;
|
|
20918
|
+
} catch {
|
|
20919
|
+
throw new InvalidRestoredSessionError("Persisted session has an invalid wallet address.");
|
|
20845
20920
|
}
|
|
20846
|
-
const restoredAddress = hasPersistedProof ? canonicalizeAddress2(sessionData.address) : void 0;
|
|
20847
20921
|
let stagedSessionExpiry = /* @__PURE__ */ new Date(0);
|
|
20848
20922
|
let stagedRecap = [];
|
|
20849
20923
|
if (hasPersistedProof) {
|
|
20850
20924
|
if (typeof this.wasmBindings.validatePersistedSession !== "function") {
|
|
20851
20925
|
throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
|
|
20852
20926
|
}
|
|
20853
|
-
const verified =
|
|
20854
|
-
|
|
20855
|
-
|
|
20856
|
-
|
|
20857
|
-
|
|
20858
|
-
|
|
20859
|
-
|
|
20860
|
-
|
|
20861
|
-
|
|
20862
|
-
|
|
20863
|
-
|
|
20927
|
+
const verified = (() => {
|
|
20928
|
+
try {
|
|
20929
|
+
return this.wasmBindings.validatePersistedSession({
|
|
20930
|
+
delegationHeader: sessionData.delegationHeader,
|
|
20931
|
+
delegationCid: sessionData.delegationCid,
|
|
20932
|
+
spaceId: sessionData.spaceId,
|
|
20933
|
+
jwk: stagedJwk,
|
|
20934
|
+
address: restoredAddress,
|
|
20935
|
+
chainId: sessionData.chainId,
|
|
20936
|
+
siwe: sessionData.siwe,
|
|
20937
|
+
signature: sessionData.signature
|
|
20938
|
+
});
|
|
20939
|
+
} catch (error) {
|
|
20940
|
+
throw new InvalidRestoredSessionError(error instanceof Error ? error.message : String(error));
|
|
20941
|
+
}
|
|
20942
|
+
})();
|
|
20943
|
+
const exactRecap = Array.isArray(verified.verifiedRecap) ? verifierRecapEntries(verified.verifiedRecap) : verified.verifiedRecap;
|
|
20864
20944
|
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
20865
20945
|
(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(
|
|
20866
20946
|
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
@@ -20880,7 +20960,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20880
20960
|
const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
|
|
20881
20961
|
const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
|
|
20882
20962
|
if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
|
|
20883
|
-
throw new
|
|
20963
|
+
throw new InvalidRestoredSessionError("Persisted session expiry does not match its signed SIWE authority.");
|
|
20884
20964
|
}
|
|
20885
20965
|
stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
|
|
20886
20966
|
}
|
|
@@ -20977,7 +21057,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20977
21057
|
operations = recap.flatMap((entry) => {
|
|
20978
21058
|
const service = this.invocationServiceName(entry.service);
|
|
20979
21059
|
return entry.actions.map((action) => ({
|
|
20980
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
|
|
21060
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId: entry.space },
|
|
20981
21061
|
service,
|
|
20982
21062
|
path: entry.path,
|
|
20983
21063
|
action,
|
|
@@ -21073,7 +21153,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21073
21153
|
const delegations = input.recap.map((entry) => ({
|
|
21074
21154
|
cid: session.delegationCid,
|
|
21075
21155
|
delegateDID: session.verificationMethod,
|
|
21076
|
-
spaceId: entry.space,
|
|
21156
|
+
spaceId: this.spaceIdsEqual(entry.space, session.spaceId) ? session.spaceId : entry.space,
|
|
21077
21157
|
path: entry.path,
|
|
21078
21158
|
actions: [...entry.actions],
|
|
21079
21159
|
caveats: cloneRecapCaveats(entry.caveats),
|
|
@@ -21763,16 +21843,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21763
21843
|
);
|
|
21764
21844
|
graph.assertActive();
|
|
21765
21845
|
if (!response.ok) {
|
|
21766
|
-
|
|
21767
|
-
|
|
21768
|
-
|
|
21769
|
-
|
|
21770
|
-
|
|
21771
|
-
const nested = typeof record?.error === "object" && record.error !== null ? record.error : void 0;
|
|
21772
|
-
permissionHint = parsePermissionHint(record?.permissionHint) ?? parsePermissionHint(nested?.permissionHint);
|
|
21773
|
-
} catch {
|
|
21774
|
-
}
|
|
21775
|
-
}
|
|
21846
|
+
const permissionHint = response.status === 401 || response.status === 403 ? parsePermissionHint({
|
|
21847
|
+
service: "tinycloud.encryption",
|
|
21848
|
+
path: networkId,
|
|
21849
|
+
actions: ["tinycloud.encryption/decrypt"]
|
|
21850
|
+
}) : void 0;
|
|
21776
21851
|
throw new DecryptTransportResponseError(response.status, permissionHint);
|
|
21777
21852
|
}
|
|
21778
21853
|
return await response.json();
|
|
@@ -22197,63 +22272,62 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22197
22272
|
}
|
|
22198
22273
|
};
|
|
22199
22274
|
}
|
|
22200
|
-
/** Register a policy
|
|
22201
|
-
async
|
|
22275
|
+
/** Register a signed policy and sibling roots with this Node's Policy/v3 runtime. */
|
|
22276
|
+
async registerPolicy(params) {
|
|
22202
22277
|
this._serviceGraph.assertActive();
|
|
22203
|
-
|
|
22204
|
-
|
|
22205
|
-
|
|
22206
|
-
|
|
22207
|
-
body: JSON.stringify({
|
|
22208
|
-
policy: { bytes: base64UrlEncode(params.policy.bytes), cid: params.policy.cid, proof: params.policy.proof },
|
|
22209
|
-
ownerDelegation: { cid: params.ownerDelegation.delegationCid, dagCbor: base64UrlEncode(params.ownerDelegation.signedDagCbor) },
|
|
22210
|
-
enforcementDelegation: params.enforcementDelegation,
|
|
22211
|
-
contentSourceDigest: params.contentSourceDigest
|
|
22212
|
-
})
|
|
22278
|
+
return registerPolicyV3({
|
|
22279
|
+
...params,
|
|
22280
|
+
nodeOrigin: this.config.host,
|
|
22281
|
+
fetch: globalThis.fetch.bind(globalThis)
|
|
22213
22282
|
});
|
|
22214
|
-
if (!response.ok) {
|
|
22215
|
-
let code = "";
|
|
22216
|
-
try {
|
|
22217
|
-
const body = await response.clone().json();
|
|
22218
|
-
if (typeof body.error?.code === "string") code = ` ${body.error.code}`;
|
|
22219
|
-
} catch {
|
|
22220
|
-
}
|
|
22221
|
-
throw new Error(`Owner share policy registration failed: ${response.status}${code}`);
|
|
22222
|
-
}
|
|
22223
|
-
const responseBytes = new Uint8Array(await response.arrayBuffer());
|
|
22224
|
-
return validateOwnerSharePolicyRegistrationBytes(responseBytes, params);
|
|
22225
22283
|
}
|
|
22226
|
-
/** Authorize
|
|
22227
|
-
async
|
|
22284
|
+
/** Authorize one short-lived v3 delivery against the signed v3 envelope and registered roots. */
|
|
22285
|
+
async authorizeShareDeliveryV3(input) {
|
|
22228
22286
|
const session = this.currentTinyCloudSession();
|
|
22229
22287
|
const serviceSession = this._serviceContext?.session;
|
|
22230
22288
|
if (!session || !serviceSession) throw new Error("Share delivery requires an authenticated session");
|
|
22289
|
+
const idempotencyBytes = new TextEncoder().encode(input.idempotencyKey);
|
|
22290
|
+
if (idempotencyBytes.byteLength === 0 || idempotencyBytes.byteLength > 512) {
|
|
22291
|
+
throw new Error("Share delivery idempotency key is invalid");
|
|
22292
|
+
}
|
|
22293
|
+
const jtiDigest = new Uint8Array(await crypto.subtle.digest("SHA-256", idempotencyBytes));
|
|
22231
22294
|
const body = {
|
|
22232
|
-
|
|
22295
|
+
envelope: input.envelope,
|
|
22296
|
+
sealedEnvelope: input.sealedEnvelope,
|
|
22297
|
+
envelopeKey: input.envelopeKey,
|
|
22233
22298
|
shareCid: input.shareCid,
|
|
22234
|
-
shareId: input.shareId,
|
|
22235
|
-
registrationCid: input.registrationCid,
|
|
22236
|
-
policyCid: input.policyCid,
|
|
22237
|
-
delegationCid: input.delegationCid,
|
|
22238
|
-
enforcementDelegationCid: input.enforcementDelegationCid,
|
|
22239
22299
|
recipientEmail: input.recipientEmail,
|
|
22240
22300
|
shareUrl: input.shareUrl,
|
|
22241
22301
|
documentName: input.documentName,
|
|
22242
|
-
jti: base64UrlEncode(
|
|
22243
|
-
|
|
22244
|
-
expiresAt: input.expiresAt
|
|
22302
|
+
jti: base64UrlEncode(jtiDigest.slice(0, 16)),
|
|
22303
|
+
expiresAt: new Date(input.expiresAt).toISOString().replace(/\.\d{3}Z$/, "Z")
|
|
22245
22304
|
};
|
|
22246
22305
|
const requestBodyDigest = base64UrlEncode(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonicalizeEncryptionJson(body)))));
|
|
22247
22306
|
const request = { ...body, requestBodyDigest };
|
|
22248
22307
|
const authorization = authorizationHeader(this.invokeAnyWithRuntimePermissions(serviceSession, [{ spaceId: session.spaceId, service: "kv", path: input.resourcePath, action: "tinycloud.kv/get" }]));
|
|
22249
|
-
const response = await fetch(`${this.config.host}/
|
|
22308
|
+
const response = await fetch(`${this.config.host}/policy/v3/deliveries/authorize`, {
|
|
22250
22309
|
method: "POST",
|
|
22251
22310
|
headers: { accept: "application/json", "content-type": "application/json", authorization },
|
|
22252
22311
|
body: canonicalizeEncryptionJson(request)
|
|
22253
22312
|
});
|
|
22254
|
-
if (!response.ok) throw new Error(`
|
|
22255
|
-
const
|
|
22256
|
-
|
|
22313
|
+
if (!response.ok) throw new Error(`V3 share delivery authorization failed: ${response.status}`);
|
|
22314
|
+
const verified = validateShareDeliveryAuthorizationV3Bytes(new Uint8Array(await response.arrayBuffer()), {
|
|
22315
|
+
request,
|
|
22316
|
+
senderKeyDid: this.credentialHolderDid,
|
|
22317
|
+
deliveryAudience: input.deliveryAudience
|
|
22318
|
+
});
|
|
22319
|
+
const requestDigest = new Uint8Array(await crypto.subtle.digest(
|
|
22320
|
+
"SHA-256",
|
|
22321
|
+
new TextEncoder().encode(`${CREDENTIAL_INVITATION_REQUEST_DOMAIN}${canonicalizeEncryptionJson(verified.request)}`)
|
|
22322
|
+
));
|
|
22323
|
+
return {
|
|
22324
|
+
...verified,
|
|
22325
|
+
proof: {
|
|
22326
|
+
alg: "EdDSA",
|
|
22327
|
+
kid: this.credentialHolderDid,
|
|
22328
|
+
signature: base64UrlEncode(await this.signSessionBytes(requestDigest))
|
|
22329
|
+
}
|
|
22330
|
+
};
|
|
22257
22331
|
}
|
|
22258
22332
|
async createRootDelegationForSharing(params, assertActive) {
|
|
22259
22333
|
try {
|
|
@@ -22708,7 +22782,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22708
22782
|
continue;
|
|
22709
22783
|
}
|
|
22710
22784
|
let space;
|
|
22711
|
-
if (service !== "tinycloud.encryption") {
|
|
22785
|
+
if (service !== "tinycloud.encryption" || operation.resource === void 0) {
|
|
22712
22786
|
if (typeof operation.spaceId !== "string") continue;
|
|
22713
22787
|
try {
|
|
22714
22788
|
space = this.resolvePermissionSpace(operation.spaceId, session);
|
|
@@ -23253,11 +23327,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23253
23327
|
);
|
|
23254
23328
|
return { delegation: delegation2, prompted: true };
|
|
23255
23329
|
}
|
|
23256
|
-
const granted =
|
|
23257
|
-
|
|
23258
|
-
session.siwe
|
|
23259
|
-
);
|
|
23260
|
-
const { subset, missing } = isCapabilitySubset(expandedEntries, granted);
|
|
23330
|
+
const granted = this.projectSignedRecapCapabilities(session.siwe);
|
|
23331
|
+
const { subset, missing } = this.signedCapabilitySubset(expandedEntries, granted);
|
|
23261
23332
|
if (!subset) {
|
|
23262
23333
|
const runtimeOperations = this.permissionEntriesToOperations(expandedEntries, session);
|
|
23263
23334
|
const runtimeGrant = this.findGrantForOperations(
|
|
@@ -23577,7 +23648,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23577
23648
|
return abilities;
|
|
23578
23649
|
}
|
|
23579
23650
|
isEncryptionPermissionEntry(entry) {
|
|
23580
|
-
return entry.service === ENCRYPTION_PERMISSION_SERVICE2 &&
|
|
23651
|
+
return entry.service === ENCRYPTION_PERMISSION_SERVICE2 && this.isEncryptionNetworkOperation("encryption", entry.path, entry.space);
|
|
23581
23652
|
}
|
|
23582
23653
|
permissionsToRawAbilities(entries) {
|
|
23583
23654
|
const rawAbilities = {};
|
|
@@ -23601,7 +23672,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23601
23672
|
return entries.flatMap((entry) => {
|
|
23602
23673
|
const service = this.shortServiceName(entry.service);
|
|
23603
23674
|
return entry.actions.map((action) => ({
|
|
23604
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
23675
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
|
|
23605
23676
|
service,
|
|
23606
23677
|
path: entry.path,
|
|
23607
23678
|
action,
|
|
@@ -23609,13 +23680,21 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23609
23680
|
}));
|
|
23610
23681
|
});
|
|
23611
23682
|
}
|
|
23683
|
+
/** Raw network authority cannot be covered by a nested encryption space. */
|
|
23684
|
+
signedCapabilitySubset(requested, granted) {
|
|
23685
|
+
const rawGranted = granted.filter((entry) => this.isEncryptionPermissionEntry(entry)).map((entry) => ({ ...entry, space: "encryption" }));
|
|
23686
|
+
const spaceGranted = granted.filter((entry) => !this.isEncryptionPermissionEntry(entry));
|
|
23687
|
+
const missing = requested.filter((entry) => {
|
|
23688
|
+
const raw = this.isEncryptionPermissionEntry(entry);
|
|
23689
|
+
const comparable = raw ? { ...entry, space: "encryption" } : entry;
|
|
23690
|
+
return !isCapabilitySubset([comparable], raw ? rawGranted : spaceGranted).subset;
|
|
23691
|
+
});
|
|
23692
|
+
return { subset: missing.length === 0, missing };
|
|
23693
|
+
}
|
|
23612
23694
|
sessionCoversPermissionEntries(session, entries) {
|
|
23613
23695
|
try {
|
|
23614
|
-
const granted =
|
|
23615
|
-
|
|
23616
|
-
session.siwe
|
|
23617
|
-
);
|
|
23618
|
-
return isCapabilitySubset(entries, granted).subset;
|
|
23696
|
+
const granted = this.projectSignedRecapCapabilities(session.siwe);
|
|
23697
|
+
return this.signedCapabilitySubset(entries, granted).subset;
|
|
23619
23698
|
} catch {
|
|
23620
23699
|
return false;
|
|
23621
23700
|
}
|
|
@@ -23625,7 +23704,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23625
23704
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
23626
23705
|
const service = this.shortServiceName(entry.service);
|
|
23627
23706
|
return entry.actions.map((action) => ({
|
|
23628
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
23707
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
|
|
23629
23708
|
service,
|
|
23630
23709
|
path: entry.path,
|
|
23631
23710
|
action,
|
|
@@ -23714,7 +23793,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23714
23793
|
return resources.flatMap((resource) => {
|
|
23715
23794
|
const service = this.invocationServiceName(resource.service);
|
|
23716
23795
|
return resource.actions.map((action) => ({
|
|
23717
|
-
...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
|
|
23796
|
+
...this.isEncryptionNetworkOperation(service, resource.path, resource.space) ? { resource: resource.path } : { spaceId: resource.space },
|
|
23718
23797
|
service,
|
|
23719
23798
|
path: resource.path,
|
|
23720
23799
|
action,
|
|
@@ -23775,7 +23854,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23775
23854
|
};
|
|
23776
23855
|
for (const resource of resources) {
|
|
23777
23856
|
const service = this.invocationServiceName(resource.service);
|
|
23778
|
-
if (this.isEncryptionNetworkOperation(service, resource.path)) {
|
|
23857
|
+
if (this.isEncryptionNetworkOperation(service, resource.path, resource.space)) {
|
|
23779
23858
|
rawAbilities[_a = resource.path] ?? (rawAbilities[_a] = []);
|
|
23780
23859
|
addActions(rawAbilities[resource.path], resource.actions);
|
|
23781
23860
|
continue;
|
|
@@ -23844,21 +23923,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23844
23923
|
}
|
|
23845
23924
|
return granted.spaceId !== void 0 && requested.spaceId !== void 0 && this.spaceIdsEqual(granted.spaceId, requested.spaceId) && this.pathContains(granted.path, requested.path);
|
|
23846
23925
|
}
|
|
23847
|
-
// Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`.
|
|
23848
|
-
// EIP-155 address
|
|
23849
|
-
//
|
|
23850
|
-
// the EIP-55 checksummed form — so a byte-for-byte compare spuriously rejects
|
|
23851
|
-
// an otherwise-valid grant. Lowercase ONLY the `eip155:<chain>:0x<addr>`
|
|
23852
|
-
// segment and leave everything else (crucially the case-sensitive space NAME)
|
|
23853
|
-
// byte-exact. Mirrors the CLI's `normalizeSpaceForCompare` (OPENKEY_SCOPE_MISMATCH fix).
|
|
23926
|
+
// Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`. Compare
|
|
23927
|
+
// only the embedded EIP-155 address without case sensitivity; chain, name,
|
|
23928
|
+
// and all non-PKH space identifiers remain byte-exact.
|
|
23854
23929
|
spaceIdsEqual(a, b) {
|
|
23855
23930
|
return this.normalizeSpaceAddress(a) === this.normalizeSpaceAddress(b);
|
|
23856
23931
|
}
|
|
23857
23932
|
normalizeSpaceAddress(space) {
|
|
23858
|
-
|
|
23859
|
-
|
|
23860
|
-
(_match, prefix, addr) => prefix + addr.toLowerCase()
|
|
23861
|
-
);
|
|
23933
|
+
const match = /^(tinycloud:pkh:eip155:\d+:)(0x[0-9a-fA-F]{40})(:.+)$/.exec(space);
|
|
23934
|
+
return match === null ? space : `${match[1]}${match[2].toLowerCase()}${match[3]}`;
|
|
23862
23935
|
}
|
|
23863
23936
|
actionContains(grantedAction, requestedAction) {
|
|
23864
23937
|
if (grantedAction === requestedAction) {
|
|
@@ -23875,10 +23948,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23875
23948
|
}
|
|
23876
23949
|
/** Prefer the v2 caveat-preserving parser while retaining old custom WASM. */
|
|
23877
23950
|
parseRecapWithCaveats(siwe) {
|
|
23878
|
-
|
|
23951
|
+
const verified = this.wasmBindings.parseVerifiedRecapFromSiwe?.(siwe);
|
|
23952
|
+
return verified == null ? this.wasmBindings.parseRecapFromSiwe(siwe) : verifierRecapEntries(verified);
|
|
23879
23953
|
}
|
|
23880
|
-
isEncryptionNetworkOperation(service, path) {
|
|
23881
|
-
return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
|
|
23954
|
+
isEncryptionNetworkOperation(service, path, space) {
|
|
23955
|
+
return service === "encryption" && (space === void 0 || space === "encryption") && path.startsWith("urn:tinycloud:encryption:");
|
|
23882
23956
|
}
|
|
23883
23957
|
operationFromInvokeAnyEntry(entry) {
|
|
23884
23958
|
const service = this.invocationServiceName(entry.service);
|
|
@@ -23891,7 +23965,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23891
23965
|
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
23892
23966
|
};
|
|
23893
23967
|
}
|
|
23894
|
-
if (this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
23968
|
+
if (entry.spaceId === void 0 && this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
23895
23969
|
return {
|
|
23896
23970
|
resource: entry.path,
|
|
23897
23971
|
service,
|
|
@@ -24176,8 +24250,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24176
24250
|
const jwk = mySession.jwk;
|
|
24177
24251
|
const { abilities, rawAbilities } = this.buildActivationAbilities(delegation);
|
|
24178
24252
|
const now = /* @__PURE__ */ new Date();
|
|
24179
|
-
const
|
|
24180
|
-
const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
|
|
24253
|
+
const expirationTime = delegation.expiry;
|
|
24181
24254
|
const prepared = this.wasmBindings.prepareSession({
|
|
24182
24255
|
abilities,
|
|
24183
24256
|
address: this.wasmBindings.ensureEip55(mySession.address),
|
|
@@ -24281,8 +24354,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24281
24354
|
);
|
|
24282
24355
|
}
|
|
24283
24356
|
const now = /* @__PURE__ */ new Date();
|
|
24284
|
-
const
|
|
24285
|
-
const requestedExpiry = new Date(now.getTime() + expiryMs);
|
|
24357
|
+
const requestedExpiry = params.expiryMs === void 0 ? parentDelegation.expiry : new Date(now.getTime() + params.expiryMs);
|
|
24286
24358
|
const actualExpiry = requestedExpiry > parentDelegation.expiry ? parentDelegation.expiry : requestedExpiry;
|
|
24287
24359
|
const abilities = {};
|
|
24288
24360
|
const rawAbilities = {};
|
|
@@ -24976,6 +25048,7 @@ import {
|
|
|
24976
25048
|
SecretsService as SecretsService2,
|
|
24977
25049
|
SECRET_NAME_RE,
|
|
24978
25050
|
canonicalizeSecretScope,
|
|
25051
|
+
parseSecretCatalogKey,
|
|
24979
25052
|
resolveSecretListPrefix as resolveSecretListPrefix2,
|
|
24980
25053
|
resolveSecretPath as resolveSecretPath3
|
|
24981
25054
|
} from "@tinycloud/sdk-core";
|
|
@@ -25086,6 +25159,7 @@ export {
|
|
|
25086
25159
|
FileSessionStorage,
|
|
25087
25160
|
HooksService3 as HooksService,
|
|
25088
25161
|
IdentityParseError,
|
|
25162
|
+
InvalidRestoredSessionError,
|
|
25089
25163
|
KVService3 as KVService,
|
|
25090
25164
|
ManifestValidationError,
|
|
25091
25165
|
MemorySessionStorage,
|
|
@@ -25165,6 +25239,7 @@ export {
|
|
|
25165
25239
|
expandActionShortNames,
|
|
25166
25240
|
expandPermissionEntries2 as expandPermissionEntries,
|
|
25167
25241
|
expandPermissionEntry,
|
|
25242
|
+
extractSiweExpiration,
|
|
25168
25243
|
generateRandomReceiverKey,
|
|
25169
25244
|
getTinyCloudDebugLogs,
|
|
25170
25245
|
grantAuthRequest,
|
|
@@ -25183,6 +25258,7 @@ export {
|
|
|
25183
25258
|
parseExpiry2 as parseExpiry,
|
|
25184
25259
|
parseNetworkId3 as parseNetworkId,
|
|
25185
25260
|
parsePkhDid,
|
|
25261
|
+
parseSecretCatalogKey,
|
|
25186
25262
|
parseSpaceUri,
|
|
25187
25263
|
pkhDid3 as pkhDid,
|
|
25188
25264
|
principalDid,
|