@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.
@@ -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
- * CID-bind and activate one compact runtime delegation.
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
- * The helper derives audience, expiry, and effective permissions from the
849
- * compact UCAN's signed payload, verifies the transport fields agree, then
850
- * invokes {@link TinyCloudNode.useRuntimeDelegation}. The node activation call
851
- * remains the authority and delegation-chain validation boundary.
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, serializeDelegation as G, InvalidRestoredSessionError as I, MemorySessionStorage as M, type NodeEventEmitterStrategy as N, type OwnerDelegationReceipt as O, type PortableDelegation as P, type RestorableSession 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 RuntimeDelegationActivator as l, type RuntimePermissionGrantOptions as m, type SecretReadInput as n, type SecretReadResult as o, type SignStrategy as p, type TinyCloudNodeConfig as q, type UnifiedOwnerRootReceipt as r, UnsupportedSessionRestoreError as s, type WasmKeyProviderConfig as t, activateCompactRuntimeDelegation as u, activateValidatedRuntimeDelegation as v, createWasmKeyProvider as w, defaultSignStrategy as x, deserializeDelegation as y, grantAuthRequest as z };
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
- * CID-bind and activate one compact runtime delegation.
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
- * The helper derives audience, expiry, and effective permissions from the
849
- * compact UCAN's signed payload, verifies the transport fields agree, then
850
- * invokes {@link TinyCloudNode.useRuntimeDelegation}. The node activation call
851
- * remains the authority and delegation-chain validation boundary.
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, serializeDelegation as G, InvalidRestoredSessionError as I, MemorySessionStorage as M, type NodeEventEmitterStrategy as N, type OwnerDelegationReceipt as O, type PortableDelegation as P, type RestorableSession 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 RuntimeDelegationActivator as l, type RuntimePermissionGrantOptions as m, type SecretReadInput as n, type SecretReadResult as o, type SignStrategy as p, type TinyCloudNodeConfig as q, type UnifiedOwnerRootReceipt as r, UnsupportedSessionRestoreError as s, type WasmKeyProviderConfig as t, activateCompactRuntimeDelegation as u, activateValidatedRuntimeDelegation as v, createWasmKeyProvider as w, defaultSignStrategy as x, deserializeDelegation as y, grantAuthRequest as z };
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, reject) => {
1735
+ return new Promise((resolve, reject2) => {
1736
1736
  const timeoutId = setTimeout(() => {
1737
- reject(new Error("Sign request timed out"));
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
- reject(
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
- const exactRecap = Array.isArray(verified.verifiedRecap) ? verifierRecapEntries(verified.verifiedRecap) : verified.verifiedRecap;
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
- issuer: payload.iss,
7654
- audience: payload.aud,
7655
- expiry,
7656
- proofs: [...payload.prf],
7657
- ...remainingRedelegationDepth === void 0 ? {} : { remainingRedelegationDepth },
7658
- permissions: canonicalPermissions,
7659
- resources: canonicalResources
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
- throw new Error(
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
- throw new Error("Runtime delegation has an invalid expiry.");
7831
+ reject("authority_invalid", "Runtime delegation has an invalid expiry.");
7702
7832
  }
7703
7833
  if (delegation.expiry.getTime() <= Date.now()) {
7704
- throw new Error("Runtime delegation is expired.");
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
- throw new Error("Runtime delegation is missing authorization bytes.");
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
- throw new Error("Runtime delegation CID does not match authorization bytes.");
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
- throw new Error("Runtime delegation is expired.");
7853
+ reject("expired", "Runtime delegation is expired.");
7717
7854
  }
7718
7855
  if (delegation.expiry.getTime() !== signed.expiry.getTime()) {
7719
- throw new Error("Runtime delegation expiry does not match signed authority.");
7856
+ reject("authority_invalid", "Runtime delegation expiry does not match signed authority.");
7720
7857
  }
7721
7858
  if (!didPrincipalsMatch(signed.audience, node.sessionDid)) {
7722
- throw new Error(
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
- throw new Error("Runtime delegation audience does not match signed authority.");
7865
+ reject("audience_mismatch", "Runtime delegation audience does not match signed authority.");
7728
7866
  }
7729
- const declaredResources = canonicalResourcesFromPortableDelegation(delegation);
7730
- if (!resourcesMatch(declaredResources, signed.resources)) {
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
- throw new Error(
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
- if (node.isSessionOnly === true && typeof node.did === "string" && (0, import_sdk_core9.principalDidEquals)(node.did, node.sessionDid) && typeof node.useDelegation === "function") {
7771
- await node.useDelegation(installedCandidate);
7772
- } else {
7773
- await node.useRuntimeDelegation(installedCandidate);
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
- throw new Error("Runtime delegation activation did not install the validated authority.");
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
- throw new Error(
7926
+ reject(
7927
+ "activation_rejected",
7784
7928
  "Runtime delegation activation installed authority that differs from the validated authority."
7785
7929
  );
7786
7930
  }