@tinycloud/node-sdk 3.1.0-beta.7 → 3.1.0-beta.8
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-BWvBTn9l.d.cts → core-CC7uDKZP.d.cts} +72 -6
- package/dist/{core-BWvBTn9l.d.ts → core-CC7uDKZP.d.ts} +72 -6
- package/dist/core.cjs +197 -53
- 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 +197 -53
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +249 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +248 -85
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -758,6 +758,17 @@ interface PortableDelegation extends Omit<Delegation, "isRevoked"> {
|
|
|
758
758
|
* fields are authoritative (legacy single-resource shape).
|
|
759
759
|
*/
|
|
760
760
|
resources?: DelegatedResource[];
|
|
761
|
+
/**
|
|
762
|
+
* The wallet-signed SIWE message and EIP-191 signature behind a
|
|
763
|
+
* `delegationHeader` that is a CACAO session grant (OpenKey `/delegate`,
|
|
764
|
+
* {@link TinyCloudNode.grantRuntimePermissions}) rather than a compact UCAN.
|
|
765
|
+
* Not authority: activation rebuilds the CACAO from them and accepts only
|
|
766
|
+
* byte-identical authorization bytes.
|
|
767
|
+
*/
|
|
768
|
+
siweProof?: {
|
|
769
|
+
siwe: string;
|
|
770
|
+
signature: string;
|
|
771
|
+
};
|
|
761
772
|
}
|
|
762
773
|
/**
|
|
763
774
|
* The transport shape `tc auth request --emit` produces and that an owner
|
|
@@ -819,6 +830,11 @@ interface RuntimeDelegationActivator {
|
|
|
819
830
|
useRuntimeDelegation(delegation: PortableDelegation): Promise<void>;
|
|
820
831
|
useDelegation?(delegation: PortableDelegation): Promise<unknown>;
|
|
821
832
|
getRuntimePermissionDelegations(): PortableDelegation[];
|
|
833
|
+
/**
|
|
834
|
+
* Verify a wallet-signed session grant issued to this runtime's live session
|
|
835
|
+
* key. Required to activate a CACAO grant; compact UCANs do not use it.
|
|
836
|
+
*/
|
|
837
|
+
verifySessionGrant?(grant: SessionGrantProof): VerifiedSessionGrant;
|
|
822
838
|
}
|
|
823
839
|
/** The installed authority returned by {@link activateValidatedRuntimeDelegation}. */
|
|
824
840
|
interface ValidatedRuntimeDelegation {
|
|
@@ -842,13 +858,51 @@ interface CompactRuntimeDelegationInput {
|
|
|
842
858
|
readonly ownerAddress: string;
|
|
843
859
|
readonly chainId: number;
|
|
844
860
|
}
|
|
861
|
+
/** A CACAO session grant and the signed SIWE proof it was built from. */
|
|
862
|
+
interface SessionGrantProof {
|
|
863
|
+
readonly delegationHeader: {
|
|
864
|
+
Authorization: string;
|
|
865
|
+
};
|
|
866
|
+
readonly delegationCid: string;
|
|
867
|
+
readonly spaceId: string;
|
|
868
|
+
readonly address: string;
|
|
869
|
+
readonly chainId: number;
|
|
870
|
+
readonly siwe: string;
|
|
871
|
+
readonly signature: string;
|
|
872
|
+
}
|
|
873
|
+
/** Authority read from a verified session grant's signed ReCap. */
|
|
874
|
+
interface VerifiedSessionGrant {
|
|
875
|
+
readonly recap: readonly {
|
|
876
|
+
service: string;
|
|
877
|
+
space: string;
|
|
878
|
+
path: string;
|
|
879
|
+
actions: string[];
|
|
880
|
+
caveats?: Record<string, unknown>[];
|
|
881
|
+
}[];
|
|
882
|
+
readonly expiresAt: Date;
|
|
883
|
+
}
|
|
845
884
|
/**
|
|
846
|
-
*
|
|
885
|
+
* Stable reasons {@link activateValidatedRuntimeDelegation} refuses a
|
|
886
|
+
* delegation for. The error message is for the caller and may quote
|
|
887
|
+
* transport fields or a node's response; the reason is safe to publish.
|
|
888
|
+
*/
|
|
889
|
+
declare const RUNTIME_DELEGATION_REJECTIONS: readonly ["host_mismatch", "expired", "cid_mismatch", "authority_invalid", "proof_missing", "proof_invalid", "owner_mismatch", "audience_mismatch", "resources_mismatch", "unsupported", "activation_rejected"];
|
|
890
|
+
type RuntimeDelegationRejection = typeof RUNTIME_DELEGATION_REJECTIONS[number];
|
|
891
|
+
declare class RuntimeDelegationRejectedError extends Error {
|
|
892
|
+
readonly reason: RuntimeDelegationRejection;
|
|
893
|
+
constructor(reason: RuntimeDelegationRejection, message: string);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* CID-bind and activate one runtime delegation.
|
|
847
897
|
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
*
|
|
898
|
+
* A compact UCAN's audience, expiry and permissions come from its signed
|
|
899
|
+
* payload, and its transport fields must agree with them. A CACAO session
|
|
900
|
+
* grant (OpenKey `/delegate`, `grantRuntimePermissions`) is verified from its
|
|
901
|
+
* `siweProof` against this runtime's live session key; its installed
|
|
902
|
+
* authority is exactly the verified ReCap, and its transport `resources` are
|
|
903
|
+
* display data that is never consulted. Either way the helper then invokes
|
|
904
|
+
* {@link TinyCloudNode.useRuntimeDelegation}; the node activation call remains
|
|
905
|
+
* the authority and delegation-chain validation boundary.
|
|
852
906
|
*
|
|
853
907
|
* `options.authorize`, when given, sees the signed capabilities before
|
|
854
908
|
* anything is activated or installed; returning `false` refuses the
|
|
@@ -1971,6 +2025,18 @@ declare class TinyCloudNode {
|
|
|
1971
2025
|
* live authority must combine this with {@link getVerifiedSessionCapabilities}.
|
|
1972
2026
|
*/
|
|
1973
2027
|
getEffectiveRuntimePermissionEntries(): PermissionEntry[];
|
|
2028
|
+
/**
|
|
2029
|
+
* Verify a wallet-signed session grant (a CACAO built from a SIWE and its
|
|
2030
|
+
* EIP-191 signature) issued to this instance's live session key. The
|
|
2031
|
+
* verifier rebuilds the CACAO from `siwe` and `signature`, so the grant is
|
|
2032
|
+
* accepted only when its authorization bytes and CID are exactly that CACAO,
|
|
2033
|
+
* its signer is `address`, its audience is this session key, it is valid
|
|
2034
|
+
* now, and its ReCap covers `spaceId` or names only raw encryption networks
|
|
2035
|
+
* (a decrypt-only grant has no space). Returns the signed ReCap and expiry.
|
|
2036
|
+
* Bindings without `validateSessionGrant` fall back to the session verifier,
|
|
2037
|
+
* which also refuses decrypt-only grants.
|
|
2038
|
+
*/
|
|
2039
|
+
verifySessionGrant(grant: SessionGrantProof): VerifiedSessionGrant;
|
|
1974
2040
|
/**
|
|
1975
2041
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
1976
2042
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -2479,4 +2545,4 @@ declare class WasmKeyProvider implements KeyProvider {
|
|
|
2479
2545
|
*/
|
|
2480
2546
|
declare function createWasmKeyProvider(sessionManager: SessionManagerWithListing): WasmKeyProvider;
|
|
2481
2547
|
|
|
2482
|
-
export { ACCEPTED_MARKER_VERSIONS as A, BOOTSTRAP_COMPLETION_MARKER_KEY as B, type CompactRuntimeDelegationInput as C, type DelegateToOptions as D, type EnsureEncryptionNetworkOptions as E, FileSessionStorage as F,
|
|
2548
|
+
export { ACCEPTED_MARKER_VERSIONS as A, BOOTSTRAP_COMPLETION_MARKER_KEY as B, type CompactRuntimeDelegationInput as C, type DelegateToOptions as D, type EnsureEncryptionNetworkOptions as E, FileSessionStorage as F, activateValidatedRuntimeDelegation as G, createWasmKeyProvider as H, InvalidRestoredSessionError as I, defaultSignStrategy as J, deserializeDelegation as K, grantAuthRequest as L, MemorySessionStorage as M, type NodeEventEmitterStrategy as N, type OwnerDelegationReceipt as O, type PortableDelegation as P, serializeDelegation as Q, RUNTIME_DELEGATION_REJECTIONS as R, type SecretPermissionHint as S, TinyCloudNode as T, type UnifiedOwnerRootInput as U, type ValidatedRuntimeDelegation as V, WasmKeyProvider as W, type AuthDelegationArtifact as a, type AuthRequestArtifact as b, BOOTSTRAP_COMPLETION_MARKER_VERSION as c, type BootstrapCompletionMarker as d, type BootstrapWarning as e, type CreateOwnerDelegationParams as f, type DelegateToResult as g, DelegatedAccess as h, type DelegationAuthority as i, NodeUserAuthorization as j, type NodeUserAuthorizationConfig as k, type RestorableSession as l, type RuntimeDelegationActivator as m, RuntimeDelegationRejectedError as n, type RuntimeDelegationRejection as o, type RuntimePermissionGrantOptions as p, type SecretReadInput as q, type SecretReadResult as r, type SessionGrantProof as s, type SignStrategy as t, type TinyCloudNodeConfig as u, type UnifiedOwnerRootReceipt as v, UnsupportedSessionRestoreError as w, type VerifiedSessionGrant as x, type WasmKeyProviderConfig as y, activateCompactRuntimeDelegation as z };
|
|
@@ -758,6 +758,17 @@ interface PortableDelegation extends Omit<Delegation, "isRevoked"> {
|
|
|
758
758
|
* fields are authoritative (legacy single-resource shape).
|
|
759
759
|
*/
|
|
760
760
|
resources?: DelegatedResource[];
|
|
761
|
+
/**
|
|
762
|
+
* The wallet-signed SIWE message and EIP-191 signature behind a
|
|
763
|
+
* `delegationHeader` that is a CACAO session grant (OpenKey `/delegate`,
|
|
764
|
+
* {@link TinyCloudNode.grantRuntimePermissions}) rather than a compact UCAN.
|
|
765
|
+
* Not authority: activation rebuilds the CACAO from them and accepts only
|
|
766
|
+
* byte-identical authorization bytes.
|
|
767
|
+
*/
|
|
768
|
+
siweProof?: {
|
|
769
|
+
siwe: string;
|
|
770
|
+
signature: string;
|
|
771
|
+
};
|
|
761
772
|
}
|
|
762
773
|
/**
|
|
763
774
|
* The transport shape `tc auth request --emit` produces and that an owner
|
|
@@ -819,6 +830,11 @@ interface RuntimeDelegationActivator {
|
|
|
819
830
|
useRuntimeDelegation(delegation: PortableDelegation): Promise<void>;
|
|
820
831
|
useDelegation?(delegation: PortableDelegation): Promise<unknown>;
|
|
821
832
|
getRuntimePermissionDelegations(): PortableDelegation[];
|
|
833
|
+
/**
|
|
834
|
+
* Verify a wallet-signed session grant issued to this runtime's live session
|
|
835
|
+
* key. Required to activate a CACAO grant; compact UCANs do not use it.
|
|
836
|
+
*/
|
|
837
|
+
verifySessionGrant?(grant: SessionGrantProof): VerifiedSessionGrant;
|
|
822
838
|
}
|
|
823
839
|
/** The installed authority returned by {@link activateValidatedRuntimeDelegation}. */
|
|
824
840
|
interface ValidatedRuntimeDelegation {
|
|
@@ -842,13 +858,51 @@ interface CompactRuntimeDelegationInput {
|
|
|
842
858
|
readonly ownerAddress: string;
|
|
843
859
|
readonly chainId: number;
|
|
844
860
|
}
|
|
861
|
+
/** A CACAO session grant and the signed SIWE proof it was built from. */
|
|
862
|
+
interface SessionGrantProof {
|
|
863
|
+
readonly delegationHeader: {
|
|
864
|
+
Authorization: string;
|
|
865
|
+
};
|
|
866
|
+
readonly delegationCid: string;
|
|
867
|
+
readonly spaceId: string;
|
|
868
|
+
readonly address: string;
|
|
869
|
+
readonly chainId: number;
|
|
870
|
+
readonly siwe: string;
|
|
871
|
+
readonly signature: string;
|
|
872
|
+
}
|
|
873
|
+
/** Authority read from a verified session grant's signed ReCap. */
|
|
874
|
+
interface VerifiedSessionGrant {
|
|
875
|
+
readonly recap: readonly {
|
|
876
|
+
service: string;
|
|
877
|
+
space: string;
|
|
878
|
+
path: string;
|
|
879
|
+
actions: string[];
|
|
880
|
+
caveats?: Record<string, unknown>[];
|
|
881
|
+
}[];
|
|
882
|
+
readonly expiresAt: Date;
|
|
883
|
+
}
|
|
845
884
|
/**
|
|
846
|
-
*
|
|
885
|
+
* Stable reasons {@link activateValidatedRuntimeDelegation} refuses a
|
|
886
|
+
* delegation for. The error message is for the caller and may quote
|
|
887
|
+
* transport fields or a node's response; the reason is safe to publish.
|
|
888
|
+
*/
|
|
889
|
+
declare const RUNTIME_DELEGATION_REJECTIONS: readonly ["host_mismatch", "expired", "cid_mismatch", "authority_invalid", "proof_missing", "proof_invalid", "owner_mismatch", "audience_mismatch", "resources_mismatch", "unsupported", "activation_rejected"];
|
|
890
|
+
type RuntimeDelegationRejection = typeof RUNTIME_DELEGATION_REJECTIONS[number];
|
|
891
|
+
declare class RuntimeDelegationRejectedError extends Error {
|
|
892
|
+
readonly reason: RuntimeDelegationRejection;
|
|
893
|
+
constructor(reason: RuntimeDelegationRejection, message: string);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* CID-bind and activate one runtime delegation.
|
|
847
897
|
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
*
|
|
898
|
+
* A compact UCAN's audience, expiry and permissions come from its signed
|
|
899
|
+
* payload, and its transport fields must agree with them. A CACAO session
|
|
900
|
+
* grant (OpenKey `/delegate`, `grantRuntimePermissions`) is verified from its
|
|
901
|
+
* `siweProof` against this runtime's live session key; its installed
|
|
902
|
+
* authority is exactly the verified ReCap, and its transport `resources` are
|
|
903
|
+
* display data that is never consulted. Either way the helper then invokes
|
|
904
|
+
* {@link TinyCloudNode.useRuntimeDelegation}; the node activation call remains
|
|
905
|
+
* the authority and delegation-chain validation boundary.
|
|
852
906
|
*
|
|
853
907
|
* `options.authorize`, when given, sees the signed capabilities before
|
|
854
908
|
* anything is activated or installed; returning `false` refuses the
|
|
@@ -1971,6 +2025,18 @@ declare class TinyCloudNode {
|
|
|
1971
2025
|
* live authority must combine this with {@link getVerifiedSessionCapabilities}.
|
|
1972
2026
|
*/
|
|
1973
2027
|
getEffectiveRuntimePermissionEntries(): PermissionEntry[];
|
|
2028
|
+
/**
|
|
2029
|
+
* Verify a wallet-signed session grant (a CACAO built from a SIWE and its
|
|
2030
|
+
* EIP-191 signature) issued to this instance's live session key. The
|
|
2031
|
+
* verifier rebuilds the CACAO from `siwe` and `signature`, so the grant is
|
|
2032
|
+
* accepted only when its authorization bytes and CID are exactly that CACAO,
|
|
2033
|
+
* its signer is `address`, its audience is this session key, it is valid
|
|
2034
|
+
* now, and its ReCap covers `spaceId` or names only raw encryption networks
|
|
2035
|
+
* (a decrypt-only grant has no space). Returns the signed ReCap and expiry.
|
|
2036
|
+
* Bindings without `validateSessionGrant` fall back to the session verifier,
|
|
2037
|
+
* which also refuses decrypt-only grants.
|
|
2038
|
+
*/
|
|
2039
|
+
verifySessionGrant(grant: SessionGrantProof): VerifiedSessionGrant;
|
|
1974
2040
|
/**
|
|
1975
2041
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
1976
2042
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -2479,4 +2545,4 @@ declare class WasmKeyProvider implements KeyProvider {
|
|
|
2479
2545
|
*/
|
|
2480
2546
|
declare function createWasmKeyProvider(sessionManager: SessionManagerWithListing): WasmKeyProvider;
|
|
2481
2547
|
|
|
2482
|
-
export { ACCEPTED_MARKER_VERSIONS as A, BOOTSTRAP_COMPLETION_MARKER_KEY as B, type CompactRuntimeDelegationInput as C, type DelegateToOptions as D, type EnsureEncryptionNetworkOptions as E, FileSessionStorage as F,
|
|
2548
|
+
export { ACCEPTED_MARKER_VERSIONS as A, BOOTSTRAP_COMPLETION_MARKER_KEY as B, type CompactRuntimeDelegationInput as C, type DelegateToOptions as D, type EnsureEncryptionNetworkOptions as E, FileSessionStorage as F, activateValidatedRuntimeDelegation as G, createWasmKeyProvider as H, InvalidRestoredSessionError as I, defaultSignStrategy as J, deserializeDelegation as K, grantAuthRequest as L, MemorySessionStorage as M, type NodeEventEmitterStrategy as N, type OwnerDelegationReceipt as O, type PortableDelegation as P, serializeDelegation as Q, RUNTIME_DELEGATION_REJECTIONS as R, type SecretPermissionHint as S, TinyCloudNode as T, type UnifiedOwnerRootInput as U, type ValidatedRuntimeDelegation as V, WasmKeyProvider as W, type AuthDelegationArtifact as a, type AuthRequestArtifact as b, BOOTSTRAP_COMPLETION_MARKER_VERSION as c, type BootstrapCompletionMarker as d, type BootstrapWarning as e, type CreateOwnerDelegationParams as f, type DelegateToResult as g, DelegatedAccess as h, type DelegationAuthority as i, NodeUserAuthorization as j, type NodeUserAuthorizationConfig as k, type RestorableSession as l, type RuntimeDelegationActivator as m, RuntimeDelegationRejectedError as n, type RuntimeDelegationRejection as o, type RuntimePermissionGrantOptions as p, type SecretReadInput as q, type SecretReadResult as r, type SessionGrantProof as s, type SignStrategy as t, type TinyCloudNodeConfig as u, type UnifiedOwnerRootReceipt as v, UnsupportedSessionRestoreError as w, type VerifiedSessionGrant as x, type WasmKeyProviderConfig as y, activateCompactRuntimeDelegation as z };
|
package/dist/core.cjs
CHANGED
|
@@ -1732,14 +1732,14 @@ var NodeUserAuthorization = class {
|
|
|
1732
1732
|
* Request signature via event emitter with timeout.
|
|
1733
1733
|
*/
|
|
1734
1734
|
requestSignatureViaEmitter(request, emitter, timeout) {
|
|
1735
|
-
return new Promise((resolve,
|
|
1735
|
+
return new Promise((resolve, reject2) => {
|
|
1736
1736
|
const timeoutId = setTimeout(() => {
|
|
1737
|
-
|
|
1737
|
+
reject2(new Error("Sign request timed out"));
|
|
1738
1738
|
}, timeout);
|
|
1739
1739
|
const respond = async (response) => {
|
|
1740
1740
|
clearTimeout(timeoutId);
|
|
1741
1741
|
if (!response.approved) {
|
|
1742
|
-
|
|
1742
|
+
reject2(
|
|
1743
1743
|
new Error(response.reason ?? "Sign request rejected via emitter")
|
|
1744
1744
|
);
|
|
1745
1745
|
} else {
|
|
@@ -2322,6 +2322,25 @@ function jsonFromVerifier(value) {
|
|
|
2322
2322
|
function verifierRecapEntries(entries) {
|
|
2323
2323
|
return entries.map((entry) => Array.isArray(entry.caveats) ? { ...entry, caveats: entry.caveats.map((caveat) => jsonFromVerifier(caveat)) } : entry);
|
|
2324
2324
|
}
|
|
2325
|
+
function exactVerifiedRecap(verified) {
|
|
2326
|
+
const exactRecap = Array.isArray(verified.verifiedRecap) ? verifierRecapEntries(verified.verifiedRecap) : verified.verifiedRecap;
|
|
2327
|
+
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
2328
|
+
(entry) => entry !== null && typeof entry === "object" && typeof entry.service === "string" && typeof entry.space === "string" && typeof entry.path === "string" && Array.isArray(entry.actions) && entry.actions.every((action) => typeof action === "string") && Array.isArray(entry.caveats) && entry.caveats.every(
|
|
2329
|
+
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
2330
|
+
)
|
|
2331
|
+
)) {
|
|
2332
|
+
throw new UnsupportedSessionRestoreError(
|
|
2333
|
+
"it cannot reconstruct caveat-preserving persisted ReCap authority"
|
|
2334
|
+
);
|
|
2335
|
+
}
|
|
2336
|
+
return exactRecap.map((entry) => ({
|
|
2337
|
+
service: entry.service,
|
|
2338
|
+
space: entry.space,
|
|
2339
|
+
path: entry.path,
|
|
2340
|
+
actions: [...entry.actions],
|
|
2341
|
+
caveats: cloneRecapCaveats(entry.caveats)
|
|
2342
|
+
}));
|
|
2343
|
+
}
|
|
2325
2344
|
var ServiceGraphLifetime = class {
|
|
2326
2345
|
constructor(invokeFn, invokeAnyFn, fetchFn) {
|
|
2327
2346
|
this.invokeFn = invokeFn;
|
|
@@ -3986,23 +4005,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3986
4005
|
throw new InvalidRestoredSessionError(error instanceof Error ? error.message : String(error));
|
|
3987
4006
|
}
|
|
3988
4007
|
})();
|
|
3989
|
-
|
|
3990
|
-
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
3991
|
-
(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(
|
|
3992
|
-
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
3993
|
-
)
|
|
3994
|
-
)) {
|
|
3995
|
-
throw new UnsupportedSessionRestoreError(
|
|
3996
|
-
"it cannot reconstruct caveat-preserving persisted ReCap authority"
|
|
3997
|
-
);
|
|
3998
|
-
}
|
|
3999
|
-
stagedRecap = exactRecap.map((entry) => ({
|
|
4000
|
-
service: entry.service,
|
|
4001
|
-
space: entry.space,
|
|
4002
|
-
path: entry.path,
|
|
4003
|
-
actions: [...entry.actions],
|
|
4004
|
-
caveats: cloneRecapCaveats(entry.caveats)
|
|
4005
|
-
}));
|
|
4008
|
+
stagedRecap = exactVerifiedRecap(verified);
|
|
4006
4009
|
const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
|
|
4007
4010
|
const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
|
|
4008
4011
|
if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
|
|
@@ -5863,6 +5866,42 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5863
5866
|
(left, right) => left.service.localeCompare(right.service) || (left.space ?? "").localeCompare(right.space ?? "") || left.path.localeCompare(right.path) || left.actions.join("\0").localeCompare(right.actions.join("\0"))
|
|
5864
5867
|
);
|
|
5865
5868
|
}
|
|
5869
|
+
/**
|
|
5870
|
+
* Verify a wallet-signed session grant (a CACAO built from a SIWE and its
|
|
5871
|
+
* EIP-191 signature) issued to this instance's live session key. The
|
|
5872
|
+
* verifier rebuilds the CACAO from `siwe` and `signature`, so the grant is
|
|
5873
|
+
* accepted only when its authorization bytes and CID are exactly that CACAO,
|
|
5874
|
+
* its signer is `address`, its audience is this session key, it is valid
|
|
5875
|
+
* now, and its ReCap covers `spaceId` or names only raw encryption networks
|
|
5876
|
+
* (a decrypt-only grant has no space). Returns the signed ReCap and expiry.
|
|
5877
|
+
* Bindings without `validateSessionGrant` fall back to the session verifier,
|
|
5878
|
+
* which also refuses decrypt-only grants.
|
|
5879
|
+
*/
|
|
5880
|
+
verifySessionGrant(grant) {
|
|
5881
|
+
const bindings = this.wasmBindings;
|
|
5882
|
+
const validate = typeof bindings.validateSessionGrant === "function" ? bindings.validateSessionGrant.bind(bindings) : bindings.validatePersistedSession?.bind(bindings);
|
|
5883
|
+
if (validate === void 0) {
|
|
5884
|
+
throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
|
|
5885
|
+
}
|
|
5886
|
+
const verified = validate({
|
|
5887
|
+
delegationHeader: { Authorization: grant.delegationHeader.Authorization },
|
|
5888
|
+
delegationCid: grant.delegationCid,
|
|
5889
|
+
spaceId: grant.spaceId,
|
|
5890
|
+
jwk: this.sessionKeyJwk,
|
|
5891
|
+
address: (0, import_sdk_core8.canonicalizeAddress)(grant.address),
|
|
5892
|
+
chainId: grant.chainId,
|
|
5893
|
+
siwe: grant.siwe,
|
|
5894
|
+
signature: grant.signature
|
|
5895
|
+
});
|
|
5896
|
+
if (typeof verified.expiresAt !== "string") {
|
|
5897
|
+
throw new Error("Session grant has no signed expiry.");
|
|
5898
|
+
}
|
|
5899
|
+
const expiresAt = new Date(verified.expiresAt);
|
|
5900
|
+
if (!Number.isFinite(expiresAt.getTime())) {
|
|
5901
|
+
throw new Error("Session grant has an invalid signed expiry.");
|
|
5902
|
+
}
|
|
5903
|
+
return { recap: exactVerifiedRecap(verified), expiresAt };
|
|
5904
|
+
}
|
|
5866
5905
|
/**
|
|
5867
5906
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
5868
5907
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -5995,7 +6034,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5995
6034
|
delegatedEntries,
|
|
5996
6035
|
spaceId,
|
|
5997
6036
|
session,
|
|
5998
|
-
expiresAt
|
|
6037
|
+
expiresAt,
|
|
6038
|
+
{ siwe: prepared.siwe, signature }
|
|
5999
6039
|
);
|
|
6000
6040
|
this.runtimePermissionGrants.push({
|
|
6001
6041
|
session: {
|
|
@@ -6781,7 +6821,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6781
6821
|
}
|
|
6782
6822
|
return grants;
|
|
6783
6823
|
}
|
|
6784
|
-
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
|
|
6824
|
+
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt, siweProof) {
|
|
6785
6825
|
const resources = this.delegatedResourcesForEntries(entries, spaceId);
|
|
6786
6826
|
const primary = resources[0];
|
|
6787
6827
|
return {
|
|
@@ -6796,7 +6836,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6796
6836
|
delegateDID: session.verificationMethod,
|
|
6797
6837
|
ownerAddress: session.address,
|
|
6798
6838
|
chainId: session.chainId,
|
|
6799
|
-
host: this.config.host
|
|
6839
|
+
host: this.config.host,
|
|
6840
|
+
siweProof
|
|
6800
6841
|
};
|
|
6801
6842
|
}
|
|
6802
6843
|
runtimeGrantFromDelegation(delegation, session) {
|
|
@@ -7492,6 +7533,16 @@ var import_sdk_core12 = require("@tinycloud/sdk-core");
|
|
|
7492
7533
|
|
|
7493
7534
|
// src/delegation.ts
|
|
7494
7535
|
var import_sdk_core9 = require("@tinycloud/sdk-core");
|
|
7536
|
+
var RuntimeDelegationRejectedError = class extends Error {
|
|
7537
|
+
constructor(reason, message) {
|
|
7538
|
+
super(message);
|
|
7539
|
+
this.name = "RuntimeDelegationRejectedError";
|
|
7540
|
+
this.reason = reason;
|
|
7541
|
+
}
|
|
7542
|
+
};
|
|
7543
|
+
function reject(reason, message) {
|
|
7544
|
+
throw new RuntimeDelegationRejectedError(reason, message);
|
|
7545
|
+
}
|
|
7495
7546
|
var SERVICE_BY_SHORT_NAME = {
|
|
7496
7547
|
kv: "tinycloud.kv",
|
|
7497
7548
|
sql: "tinycloud.sql",
|
|
@@ -7638,6 +7689,15 @@ function signedAuthorityFromCompactUcan(authorization) {
|
|
|
7638
7689
|
abilities
|
|
7639
7690
|
));
|
|
7640
7691
|
}
|
|
7692
|
+
return canonicalSignedAuthority({
|
|
7693
|
+
issuer: payload.iss,
|
|
7694
|
+
audience: payload.aud,
|
|
7695
|
+
expiry,
|
|
7696
|
+
proofs: [...payload.prf],
|
|
7697
|
+
...remainingRedelegationDepth === void 0 ? {} : { remainingRedelegationDepth }
|
|
7698
|
+
}, permissions);
|
|
7699
|
+
}
|
|
7700
|
+
function canonicalSignedAuthority(signed, permissions) {
|
|
7641
7701
|
if (permissions.length === 0) {
|
|
7642
7702
|
throw new Error("Validated runtime delegation has no signed capabilities.");
|
|
7643
7703
|
}
|
|
@@ -7649,15 +7709,84 @@ function signedAuthorityFromCompactUcan(authorization) {
|
|
|
7649
7709
|
actions: [...permission.actions],
|
|
7650
7710
|
...permission.caveats === void 0 ? {} : { caveats: permission.caveats }
|
|
7651
7711
|
}));
|
|
7652
|
-
return {
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7712
|
+
return { ...signed, permissions: canonicalPermissions, resources: canonicalResources };
|
|
7713
|
+
}
|
|
7714
|
+
var RAW_ENCRYPTION_PREFIX = "urn:tinycloud:encryption:";
|
|
7715
|
+
var PKH_SPACE = /^tinycloud:pkh:eip155:(\d+):(0x[0-9a-fA-F]{40}):(.+)$/;
|
|
7716
|
+
function comparableSpace(space) {
|
|
7717
|
+
const match = PKH_SPACE.exec(space);
|
|
7718
|
+
return match ? `tinycloud:pkh:eip155:${match[1]}:${match[2].toLowerCase()}:${match[3]}` : space;
|
|
7719
|
+
}
|
|
7720
|
+
function signedAuthorityFromSessionGrant(node, delegation, authorization, cid) {
|
|
7721
|
+
const proof = delegation.siweProof;
|
|
7722
|
+
if (proof === void 0 || proof === null || typeof proof !== "object" || typeof proof.siwe !== "string" || typeof proof.signature !== "string") {
|
|
7723
|
+
reject(
|
|
7724
|
+
"proof_missing",
|
|
7725
|
+
"Runtime delegation is a wallet-signed session grant stored without its signed SIWE proof, so its authority cannot be verified."
|
|
7726
|
+
);
|
|
7727
|
+
}
|
|
7728
|
+
if (typeof node.verifySessionGrant !== "function") {
|
|
7729
|
+
reject("unsupported", "This runtime cannot verify wallet-signed session grants.");
|
|
7730
|
+
}
|
|
7731
|
+
const owner = `did:pkh:eip155:${delegation.chainId}:${delegation.ownerAddress}`;
|
|
7732
|
+
if (typeof node.did !== "string" || !didPrincipalsMatch(owner, node.did)) {
|
|
7733
|
+
reject("owner_mismatch", "Runtime delegation is not signed by this session's owner.");
|
|
7734
|
+
}
|
|
7735
|
+
let verified;
|
|
7736
|
+
try {
|
|
7737
|
+
verified = node.verifySessionGrant({
|
|
7738
|
+
delegationHeader: { Authorization: authorization },
|
|
7739
|
+
delegationCid: cid,
|
|
7740
|
+
spaceId: delegation.spaceId,
|
|
7741
|
+
address: delegation.ownerAddress,
|
|
7742
|
+
chainId: delegation.chainId,
|
|
7743
|
+
siwe: proof.siwe,
|
|
7744
|
+
signature: proof.signature
|
|
7745
|
+
});
|
|
7746
|
+
} catch (error) {
|
|
7747
|
+
reject("proof_invalid", error instanceof Error ? error.message : String(error));
|
|
7748
|
+
}
|
|
7749
|
+
const space = comparableSpace(delegation.spaceId);
|
|
7750
|
+
const spaceOwner = PKH_SPACE.exec(delegation.spaceId);
|
|
7751
|
+
if (!spaceOwner || !didPrincipalsMatch(`did:pkh:eip155:${spaceOwner[1]}:${spaceOwner[2]}`, owner)) {
|
|
7752
|
+
reject("authority_invalid", "Runtime delegation space is not owned by its signer.");
|
|
7753
|
+
}
|
|
7754
|
+
const groups = /* @__PURE__ */ new Map();
|
|
7755
|
+
for (const entry of verified.recap) {
|
|
7756
|
+
const service = entry.service.startsWith("tinycloud.") ? entry.service : `tinycloud.${entry.service}`;
|
|
7757
|
+
const raw = entry.space === "encryption";
|
|
7758
|
+
if (raw) {
|
|
7759
|
+
if (service !== "tinycloud.encryption" || !entry.path.startsWith(RAW_ENCRYPTION_PREFIX)) {
|
|
7760
|
+
reject("authority_invalid", "Runtime delegation has an unsupported signed raw resource.");
|
|
7761
|
+
}
|
|
7762
|
+
const networkOwner = entry.path.slice(RAW_ENCRYPTION_PREFIX.length, entry.path.lastIndexOf(":"));
|
|
7763
|
+
if (!didPrincipalsMatch(networkOwner, owner)) {
|
|
7764
|
+
reject("authority_invalid", "Runtime delegation names an encryption network its signer does not own.");
|
|
7765
|
+
}
|
|
7766
|
+
} else if (service === "tinycloud.encryption" || comparableSpace(entry.space) !== space) {
|
|
7767
|
+
reject("authority_invalid", "Runtime delegation has signed authority outside its space.");
|
|
7768
|
+
}
|
|
7769
|
+
const caveats = signedCaveats(entry.caveats ?? []);
|
|
7770
|
+
const key = JSON.stringify([service, raw ? "" : entry.space, entry.path, (0, import_sdk_core9.canonicalizeRecapCaveats)(caveats)]);
|
|
7771
|
+
const group = groups.get(key) ?? {
|
|
7772
|
+
service,
|
|
7773
|
+
...raw ? {} : { space: entry.space },
|
|
7774
|
+
path: entry.path,
|
|
7775
|
+
actions: [],
|
|
7776
|
+
...caveats === void 0 ? {} : { caveats }
|
|
7777
|
+
};
|
|
7778
|
+
for (const action of entry.actions) {
|
|
7779
|
+
if (!group.actions.includes(action)) group.actions.push(action);
|
|
7780
|
+
}
|
|
7781
|
+
groups.set(key, group);
|
|
7782
|
+
}
|
|
7783
|
+
return canonicalSignedAuthority({
|
|
7784
|
+
issuer: owner,
|
|
7785
|
+
// The verifier bound the SIWE audience to this runtime's own session key.
|
|
7786
|
+
audience: node.sessionDid,
|
|
7787
|
+
expiry: verified.expiresAt,
|
|
7788
|
+
proofs: []
|
|
7789
|
+
}, [...groups.values()]);
|
|
7661
7790
|
}
|
|
7662
7791
|
function canonicalResourcesFromPortableDelegation(delegation) {
|
|
7663
7792
|
if (delegation.resources !== void 0 && delegation.resources.length > 0) {
|
|
@@ -7693,49 +7822,58 @@ function resourcesMatch(actual, expected) {
|
|
|
7693
7822
|
async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
7694
7823
|
const host = normalizedHost(options.host);
|
|
7695
7824
|
if (delegation.host !== void 0 && normalizedHost(delegation.host) !== host) {
|
|
7696
|
-
|
|
7825
|
+
reject(
|
|
7826
|
+
"host_mismatch",
|
|
7697
7827
|
`Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`
|
|
7698
7828
|
);
|
|
7699
7829
|
}
|
|
7700
7830
|
if (!(delegation.expiry instanceof Date) || Number.isNaN(delegation.expiry.getTime())) {
|
|
7701
|
-
|
|
7831
|
+
reject("authority_invalid", "Runtime delegation has an invalid expiry.");
|
|
7702
7832
|
}
|
|
7703
7833
|
if (delegation.expiry.getTime() <= Date.now()) {
|
|
7704
|
-
|
|
7834
|
+
reject("expired", "Runtime delegation is expired.");
|
|
7705
7835
|
}
|
|
7706
7836
|
const authorization = delegation.delegationHeader?.Authorization;
|
|
7707
7837
|
if (typeof authorization !== "string" || authorization.length === 0) {
|
|
7708
|
-
|
|
7838
|
+
reject("authority_invalid", "Runtime delegation is missing authorization bytes.");
|
|
7709
7839
|
}
|
|
7710
7840
|
const cid = node.computeDelegationCid(authorization);
|
|
7711
7841
|
if (cid !== delegation.cid) {
|
|
7712
|
-
|
|
7842
|
+
reject("cid_mismatch", "Runtime delegation CID does not match authorization bytes.");
|
|
7843
|
+
}
|
|
7844
|
+
const compact = authorizationWithoutBearer(authorization).split(".").length === 3;
|
|
7845
|
+
let signed;
|
|
7846
|
+
try {
|
|
7847
|
+
signed = compact ? signedAuthorityFromCompactUcan(authorization) : signedAuthorityFromSessionGrant(node, delegation, authorization, cid);
|
|
7848
|
+
} catch (error) {
|
|
7849
|
+
if (error instanceof RuntimeDelegationRejectedError) throw error;
|
|
7850
|
+
reject("authority_invalid", error instanceof Error ? error.message : String(error));
|
|
7713
7851
|
}
|
|
7714
|
-
const signed = signedAuthorityFromCompactUcan(authorization);
|
|
7715
7852
|
if (signed.expiry.getTime() <= Date.now()) {
|
|
7716
|
-
|
|
7853
|
+
reject("expired", "Runtime delegation is expired.");
|
|
7717
7854
|
}
|
|
7718
7855
|
if (delegation.expiry.getTime() !== signed.expiry.getTime()) {
|
|
7719
|
-
|
|
7856
|
+
reject("authority_invalid", "Runtime delegation expiry does not match signed authority.");
|
|
7720
7857
|
}
|
|
7721
7858
|
if (!didPrincipalsMatch(signed.audience, node.sessionDid)) {
|
|
7722
|
-
|
|
7859
|
+
reject(
|
|
7860
|
+
"audience_mismatch",
|
|
7723
7861
|
`Runtime delegation targets ${signed.audience} but this session is ${node.sessionDid}.`
|
|
7724
7862
|
);
|
|
7725
7863
|
}
|
|
7726
7864
|
if (!didPrincipalsMatch(delegation.delegateDID, signed.audience)) {
|
|
7727
|
-
|
|
7865
|
+
reject("audience_mismatch", "Runtime delegation audience does not match signed authority.");
|
|
7728
7866
|
}
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
throw new Error("Runtime delegation resources do not match signed authority.");
|
|
7867
|
+
if (compact && !resourcesMatch(canonicalResourcesFromPortableDelegation(delegation), signed.resources)) {
|
|
7868
|
+
reject("resources_mismatch", "Runtime delegation resources do not match signed authority.");
|
|
7732
7869
|
}
|
|
7733
7870
|
if (options.authorize !== void 0 && !options.authorize(signed.permissions)) {
|
|
7734
7871
|
throw new Error("Runtime delegation is not authorized for this runtime.");
|
|
7735
7872
|
}
|
|
7736
7873
|
const previouslyInstalled = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
7737
7874
|
if (previouslyInstalled !== void 0 && previouslyInstalled.delegationHeader.Authorization !== authorization) {
|
|
7738
|
-
|
|
7875
|
+
reject(
|
|
7876
|
+
"cid_mismatch",
|
|
7739
7877
|
"A different authorization is already installed for this runtime delegation CID."
|
|
7740
7878
|
);
|
|
7741
7879
|
}
|
|
@@ -7765,22 +7903,28 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
|
7765
7903
|
})),
|
|
7766
7904
|
expiry: new Date(signed.expiry),
|
|
7767
7905
|
delegateDID: signed.audience,
|
|
7768
|
-
host
|
|
7906
|
+
host,
|
|
7907
|
+
...compact ? {} : { siweProof: delegation.siweProof }
|
|
7769
7908
|
};
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7909
|
+
try {
|
|
7910
|
+
if (node.isSessionOnly === true && typeof node.did === "string" && (0, import_sdk_core9.principalDidEquals)(node.did, node.sessionDid) && typeof node.useDelegation === "function") {
|
|
7911
|
+
await node.useDelegation(installedCandidate);
|
|
7912
|
+
} else {
|
|
7913
|
+
await node.useRuntimeDelegation(installedCandidate);
|
|
7914
|
+
}
|
|
7915
|
+
} catch (error) {
|
|
7916
|
+
reject("activation_rejected", error instanceof Error ? error.message : String(error));
|
|
7774
7917
|
}
|
|
7775
7918
|
const installed = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
7776
7919
|
if (!installed) {
|
|
7777
|
-
|
|
7920
|
+
reject("activation_rejected", "Runtime delegation activation did not install the validated authority.");
|
|
7778
7921
|
}
|
|
7779
7922
|
if (installed.delegationHeader.Authorization !== authorization || !didPrincipalsMatch(installed.delegateDID, signed.audience) || installed.expiry.getTime() !== signed.expiry.getTime() || !resourcesMatch(
|
|
7780
7923
|
canonicalResourcesFromPortableDelegation(installed),
|
|
7781
7924
|
signed.resources
|
|
7782
7925
|
) || normalizedHost(installed.host ?? "") !== host) {
|
|
7783
|
-
|
|
7926
|
+
reject(
|
|
7927
|
+
"activation_rejected",
|
|
7784
7928
|
"Runtime delegation activation installed authority that differs from the validated authority."
|
|
7785
7929
|
);
|
|
7786
7930
|
}
|