@t2000/sdk 5.17.0 → 5.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -76,6 +76,22 @@ interface VerifyAnchor {
76
76
  anchoredBy?: string;
77
77
  explorer: string;
78
78
  }
79
+ /** A typed TCB claim from the attested upstream (dstack ACI §tcb-and-claims). */
80
+ interface UpstreamClaim {
81
+ name: string;
82
+ status: string;
83
+ source?: string;
84
+ }
85
+ interface VerifyUpstream {
86
+ provider?: string;
87
+ modelId?: string;
88
+ result?: string;
89
+ tcbStatus?: string;
90
+ /** The attested-session id (`as_…`) — resolve at GET /v1/aci/sessions/{id}. */
91
+ sessionId?: string;
92
+ /** Typed TCB claims (tee_attested, tcb_up_to_date, gpu_attested, …). */
93
+ claims?: UpstreamClaim[];
94
+ }
79
95
  interface VerifyResult {
80
96
  receiptId: string;
81
97
  /** True iff the receipt resolved AND its on-chain Sui anchor matches. */
@@ -85,11 +101,7 @@ interface VerifyResult {
85
101
  checks: VerifyCheck[];
86
102
  wireHash?: string;
87
103
  workloadId?: string;
88
- upstream?: {
89
- provider?: string;
90
- result?: string;
91
- tcbStatus?: string;
92
- };
104
+ upstream?: VerifyUpstream;
93
105
  anchor?: VerifyAnchor;
94
106
  }
95
107
  interface VerifyOptions {
@@ -97,6 +109,9 @@ interface VerifyOptions {
97
109
  apiBase?: string;
98
110
  /** Sui network for the trustless anchor read (default `mainnet`). */
99
111
  network?: 'mainnet' | 'testnet';
112
+ /** Confidential model id for fetching the attested receipt-signing key
113
+ * (default `phala/glm-5.2`; the gateway key is workload-wide). */
114
+ model?: string;
100
115
  }
101
116
  /**
102
117
  * Verify a confidential response by its receipt id. Reads the signed receipt
@@ -1067,4 +1082,4 @@ declare function fullHandle(label: string, parentName?: string): string;
1067
1082
  */
1068
1083
  declare function displayHandle(label: string, parentName?: string): string;
1069
1084
 
1070
- export { AGENT_ID_PARENT, AGENT_ID_PARENT_NAME, AGENT_ID_PARENT_NFT_ID, AUDRIC_PARENT, AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, type ApiModel, type AppenderContext, BalanceResponse, type BuildAddLeafParams, type BuildRevokeLeafParams, type ChatMessage, type ChatParams, type ChatResult, type ChatUsage, type CheckStatus, type ComposeTxOptions, type ComposeTxResult, DEFAULT_API_BASE, type DailySpend, DepositInfo, InvalidAddressError, type LabelValidationResult, type LimitAssertInput, LimitEnforcer, LimitExceededError, type LimitKind, type LimitOperation, type LimitsConfig, type LimitsFile, type NormalizedAddress, OverlayFeeConfig, PayOptions, PayResult, PaymentRequest, SPONSORED_PYTH_DEPENDENT_PROVIDERS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SendResult, type SendTransferInput, SendableAsset, type StepPreview, SuinsNotRegisteredError, type SuinsParent, SuinsRpcError, SupportedAsset, type SwapExecuteInput, SwapQuoteResult, SwapResult, SwapRouteResult, T2000, T2000Error, T2000Options, TransactionRecord, TransactionSigner, type TrustMode, type VerifyAnchor, type VerifyCheck, type VerifyOptions, type VerifyResult, WRITE_APPENDER_REGISTRY, type WriteStep, type WriteToolName, ZkLoginProof, approxUsdValue, assertLimitConfig, buildAddLeafTx, buildRevokeLeafTx, chatCompletion, chatCompletionStream, clearLimits, composeTx, dailySpentToday, deriveAllowedAddressesFromPtb, displayHandle, exportPrivateKey, fullHandle, generateKeypair, getAddress, getLimits, getSponsoredSwapProviders, getSwapQuote, hasLimits, keypairFromPrivateKey, listModels, loadKey, looksLikeSuiNs, normalizeAddressInput, queryBalance, readLimitsFile, recordDailySpend, resolveAddressToSuinsViaRpc, resolveSuinsViaRpc, saveBech32, saveKey, setLimits, validateLabel, verifyReceipt, walletExists, writeLimitsFile };
1085
+ export { AGENT_ID_PARENT, AGENT_ID_PARENT_NAME, AGENT_ID_PARENT_NFT_ID, AUDRIC_PARENT, AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, type ApiModel, type AppenderContext, BalanceResponse, type BuildAddLeafParams, type BuildRevokeLeafParams, type ChatMessage, type ChatParams, type ChatResult, type ChatUsage, type CheckStatus, type ComposeTxOptions, type ComposeTxResult, DEFAULT_API_BASE, type DailySpend, DepositInfo, InvalidAddressError, type LabelValidationResult, type LimitAssertInput, LimitEnforcer, LimitExceededError, type LimitKind, type LimitOperation, type LimitsConfig, type LimitsFile, type NormalizedAddress, OverlayFeeConfig, PayOptions, PayResult, PaymentRequest, SPONSORED_PYTH_DEPENDENT_PROVIDERS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SendResult, type SendTransferInput, SendableAsset, type StepPreview, SuinsNotRegisteredError, type SuinsParent, SuinsRpcError, SupportedAsset, type SwapExecuteInput, SwapQuoteResult, SwapResult, SwapRouteResult, T2000, T2000Error, T2000Options, TransactionRecord, TransactionSigner, type TrustMode, type UpstreamClaim, type VerifyAnchor, type VerifyCheck, type VerifyOptions, type VerifyResult, type VerifyUpstream, WRITE_APPENDER_REGISTRY, type WriteStep, type WriteToolName, ZkLoginProof, approxUsdValue, assertLimitConfig, buildAddLeafTx, buildRevokeLeafTx, chatCompletion, chatCompletionStream, clearLimits, composeTx, dailySpentToday, deriveAllowedAddressesFromPtb, displayHandle, exportPrivateKey, fullHandle, generateKeypair, getAddress, getLimits, getSponsoredSwapProviders, getSwapQuote, hasLimits, keypairFromPrivateKey, listModels, loadKey, looksLikeSuiNs, normalizeAddressInput, queryBalance, readLimitsFile, recordDailySpend, resolveAddressToSuinsViaRpc, resolveSuinsViaRpc, saveBech32, saveKey, setLimits, validateLabel, verifyReceipt, walletExists, writeLimitsFile };
package/dist/index.d.ts CHANGED
@@ -76,6 +76,22 @@ interface VerifyAnchor {
76
76
  anchoredBy?: string;
77
77
  explorer: string;
78
78
  }
79
+ /** A typed TCB claim from the attested upstream (dstack ACI §tcb-and-claims). */
80
+ interface UpstreamClaim {
81
+ name: string;
82
+ status: string;
83
+ source?: string;
84
+ }
85
+ interface VerifyUpstream {
86
+ provider?: string;
87
+ modelId?: string;
88
+ result?: string;
89
+ tcbStatus?: string;
90
+ /** The attested-session id (`as_…`) — resolve at GET /v1/aci/sessions/{id}. */
91
+ sessionId?: string;
92
+ /** Typed TCB claims (tee_attested, tcb_up_to_date, gpu_attested, …). */
93
+ claims?: UpstreamClaim[];
94
+ }
79
95
  interface VerifyResult {
80
96
  receiptId: string;
81
97
  /** True iff the receipt resolved AND its on-chain Sui anchor matches. */
@@ -85,11 +101,7 @@ interface VerifyResult {
85
101
  checks: VerifyCheck[];
86
102
  wireHash?: string;
87
103
  workloadId?: string;
88
- upstream?: {
89
- provider?: string;
90
- result?: string;
91
- tcbStatus?: string;
92
- };
104
+ upstream?: VerifyUpstream;
93
105
  anchor?: VerifyAnchor;
94
106
  }
95
107
  interface VerifyOptions {
@@ -97,6 +109,9 @@ interface VerifyOptions {
97
109
  apiBase?: string;
98
110
  /** Sui network for the trustless anchor read (default `mainnet`). */
99
111
  network?: 'mainnet' | 'testnet';
112
+ /** Confidential model id for fetching the attested receipt-signing key
113
+ * (default `phala/glm-5.2`; the gateway key is workload-wide). */
114
+ model?: string;
100
115
  }
101
116
  /**
102
117
  * Verify a confidential response by its receipt id. Reads the signed receipt
@@ -1067,4 +1082,4 @@ declare function fullHandle(label: string, parentName?: string): string;
1067
1082
  */
1068
1083
  declare function displayHandle(label: string, parentName?: string): string;
1069
1084
 
1070
- export { AGENT_ID_PARENT, AGENT_ID_PARENT_NAME, AGENT_ID_PARENT_NFT_ID, AUDRIC_PARENT, AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, type ApiModel, type AppenderContext, BalanceResponse, type BuildAddLeafParams, type BuildRevokeLeafParams, type ChatMessage, type ChatParams, type ChatResult, type ChatUsage, type CheckStatus, type ComposeTxOptions, type ComposeTxResult, DEFAULT_API_BASE, type DailySpend, DepositInfo, InvalidAddressError, type LabelValidationResult, type LimitAssertInput, LimitEnforcer, LimitExceededError, type LimitKind, type LimitOperation, type LimitsConfig, type LimitsFile, type NormalizedAddress, OverlayFeeConfig, PayOptions, PayResult, PaymentRequest, SPONSORED_PYTH_DEPENDENT_PROVIDERS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SendResult, type SendTransferInput, SendableAsset, type StepPreview, SuinsNotRegisteredError, type SuinsParent, SuinsRpcError, SupportedAsset, type SwapExecuteInput, SwapQuoteResult, SwapResult, SwapRouteResult, T2000, T2000Error, T2000Options, TransactionRecord, TransactionSigner, type TrustMode, type VerifyAnchor, type VerifyCheck, type VerifyOptions, type VerifyResult, WRITE_APPENDER_REGISTRY, type WriteStep, type WriteToolName, ZkLoginProof, approxUsdValue, assertLimitConfig, buildAddLeafTx, buildRevokeLeafTx, chatCompletion, chatCompletionStream, clearLimits, composeTx, dailySpentToday, deriveAllowedAddressesFromPtb, displayHandle, exportPrivateKey, fullHandle, generateKeypair, getAddress, getLimits, getSponsoredSwapProviders, getSwapQuote, hasLimits, keypairFromPrivateKey, listModels, loadKey, looksLikeSuiNs, normalizeAddressInput, queryBalance, readLimitsFile, recordDailySpend, resolveAddressToSuinsViaRpc, resolveSuinsViaRpc, saveBech32, saveKey, setLimits, validateLabel, verifyReceipt, walletExists, writeLimitsFile };
1085
+ export { AGENT_ID_PARENT, AGENT_ID_PARENT_NAME, AGENT_ID_PARENT_NFT_ID, AUDRIC_PARENT, AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, type ApiModel, type AppenderContext, BalanceResponse, type BuildAddLeafParams, type BuildRevokeLeafParams, type ChatMessage, type ChatParams, type ChatResult, type ChatUsage, type CheckStatus, type ComposeTxOptions, type ComposeTxResult, DEFAULT_API_BASE, type DailySpend, DepositInfo, InvalidAddressError, type LabelValidationResult, type LimitAssertInput, LimitEnforcer, LimitExceededError, type LimitKind, type LimitOperation, type LimitsConfig, type LimitsFile, type NormalizedAddress, OverlayFeeConfig, PayOptions, PayResult, PaymentRequest, SPONSORED_PYTH_DEPENDENT_PROVIDERS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SendResult, type SendTransferInput, SendableAsset, type StepPreview, SuinsNotRegisteredError, type SuinsParent, SuinsRpcError, SupportedAsset, type SwapExecuteInput, SwapQuoteResult, SwapResult, SwapRouteResult, T2000, T2000Error, T2000Options, TransactionRecord, TransactionSigner, type TrustMode, type UpstreamClaim, type VerifyAnchor, type VerifyCheck, type VerifyOptions, type VerifyResult, type VerifyUpstream, WRITE_APPENDER_REGISTRY, type WriteStep, type WriteToolName, ZkLoginProof, approxUsdValue, assertLimitConfig, buildAddLeafTx, buildRevokeLeafTx, chatCompletion, chatCompletionStream, clearLimits, composeTx, dailySpentToday, deriveAllowedAddressesFromPtb, displayHandle, exportPrivateKey, fullHandle, generateKeypair, getAddress, getLimits, getSponsoredSwapProviders, getSwapQuote, hasLimits, keypairFromPrivateKey, listModels, loadKey, looksLikeSuiNs, normalizeAddressInput, queryBalance, readLimitsFile, recordDailySpend, resolveAddressToSuinsViaRpc, resolveSuinsViaRpc, saveBech32, saveKey, setLimits, validateLabel, verifyReceipt, walletExists, writeLimitsFile };
package/dist/index.js CHANGED
@@ -11,6 +11,9 @@ import { decodeSuiPrivateKey } from '@mysten/sui/cryptography';
11
11
  import { access, mkdir, writeFile, readFile } from 'fs/promises';
12
12
  import { join, dirname, resolve } from 'path';
13
13
  import { homedir } from 'os';
14
+ import { secp256k1 } from '@noble/curves/secp256k1';
15
+ import { sha256 } from '@noble/hashes/sha256';
16
+ import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
14
17
  import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
15
18
  import { SuinsTransaction } from '@mysten/suins';
16
19
 
@@ -2025,9 +2028,85 @@ async function listModels(opts) {
2025
2028
  }));
2026
2029
  }
2027
2030
  var RECEIPT_ANCHORED_SUFFIX = "::anchor::ReceiptAnchored";
2031
+ function normalizeClaims(claims) {
2032
+ if (!claims) {
2033
+ return [];
2034
+ }
2035
+ if (Array.isArray(claims)) {
2036
+ return claims.filter((c) => c.name).map((c) => ({
2037
+ name: c.name,
2038
+ status: c.status ?? "unknown",
2039
+ source: c.source
2040
+ }));
2041
+ }
2042
+ return Object.entries(claims).map(([name, v]) => ({
2043
+ name,
2044
+ status: v?.status ?? "unknown",
2045
+ source: v?.source
2046
+ }));
2047
+ }
2028
2048
  function fullnodeUrl(network) {
2029
2049
  return network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
2030
2050
  }
2051
+ function jcs(value) {
2052
+ if (value === null) {
2053
+ return "null";
2054
+ }
2055
+ if (typeof value === "boolean") {
2056
+ return value ? "true" : "false";
2057
+ }
2058
+ if (typeof value === "number") {
2059
+ if (!Number.isInteger(value)) {
2060
+ throw new Error("JCS: non-integer number");
2061
+ }
2062
+ return String(value);
2063
+ }
2064
+ if (typeof value === "string") {
2065
+ return JSON.stringify(value);
2066
+ }
2067
+ if (Array.isArray(value)) {
2068
+ return `[${value.map(jcs).join(",")}]`;
2069
+ }
2070
+ const keys = Object.keys(value).sort();
2071
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${jcs(value[k])}`).join(",")}}`;
2072
+ }
2073
+ function verifyReceiptSignature(receipt, signingKeyHex) {
2074
+ try {
2075
+ const sig = receipt.signature;
2076
+ if (sig?.algo !== "ecdsa-secp256k1" || !sig.value) {
2077
+ return false;
2078
+ }
2079
+ const canonical = {
2080
+ api_version: receipt.api_version ?? "",
2081
+ receipt_id: receipt.receipt_id ?? "",
2082
+ chat_id: receipt.chat_id ?? null,
2083
+ workload_id: receipt.workload_id ?? "",
2084
+ workload_keyset_digest: receipt.workload_keyset_digest ?? "",
2085
+ endpoint: receipt.endpoint ?? "",
2086
+ method: receipt.method ?? "",
2087
+ served_at: receipt.served_at ?? 0,
2088
+ event_log: receipt.event_log ?? [],
2089
+ signature: { algo: sig.algo, key_id: sig.key_id ?? "" }
2090
+ };
2091
+ const prehash = sha256(new TextEncoder().encode(jcs(canonical)));
2092
+ const sigBytes = hexToBytes(sig.value);
2093
+ if (sigBytes.length !== 65) {
2094
+ return false;
2095
+ }
2096
+ let v = sigBytes[64];
2097
+ if (v >= 27 && v <= 30) {
2098
+ v -= 27;
2099
+ }
2100
+ if (v > 3) {
2101
+ return false;
2102
+ }
2103
+ const recovered = secp256k1.Signature.fromCompact(sigBytes.slice(0, 64)).addRecoveryBit(v).recoverPublicKey(prehash).toHex(false);
2104
+ const endorsed = bytesToHex(hexToBytes(signingKeyHex.replace(/^0x/, "")));
2105
+ return recovered.toLowerCase() === endorsed.toLowerCase();
2106
+ } catch {
2107
+ return false;
2108
+ }
2109
+ }
2031
2110
  async function verifyReceipt(receiptId, opts = {}) {
2032
2111
  const base = opts.apiBase ?? DEFAULT_API_BASE;
2033
2112
  const network = opts.network ?? "mainnet";
@@ -2059,6 +2138,7 @@ async function verifyReceipt(receiptId, opts = {}) {
2059
2138
  });
2060
2139
  const upstreamEv = receipt.event_log.find((e) => e.type === "upstream.verified");
2061
2140
  const upstreamOk = upstreamEv?.result === "verified";
2141
+ const claims = normalizeClaims(upstreamEv?.claims);
2062
2142
  checks.push({
2063
2143
  name: "Confidential upstream",
2064
2144
  status: upstreamEv ? upstreamOk ? "pass" : "fail" : "skip",
@@ -2121,23 +2201,50 @@ async function verifyReceipt(receiptId, opts = {}) {
2121
2201
  });
2122
2202
  }
2123
2203
  }
2204
+ let signatureForged = false;
2205
+ let sigStatus = "skip";
2206
+ let sigDetail = "no signature on receipt";
2207
+ if (receipt.signature?.value) {
2208
+ try {
2209
+ const model = opts.model ?? "phala/glm-5.2";
2210
+ const res = await fetch(
2211
+ `${base}/aci/attestation?model=${encodeURIComponent(model)}`
2212
+ );
2213
+ const att = res.ok ? await res.json() : null;
2214
+ if (!att?.signingKey) {
2215
+ sigDetail = "could not fetch the attested keyset to check the signature";
2216
+ } else if (att.workloadId && att.workloadId !== workloadId) {
2217
+ sigDetail = `attested keyset is for a different workload \u2014 pass --model for ${workloadId}`;
2218
+ } else {
2219
+ const ok = verifyReceiptSignature(receipt, att.signingKey);
2220
+ sigStatus = ok ? "pass" : "fail";
2221
+ signatureForged = !ok;
2222
+ sigDetail = ok ? `signed by the attested receipt key (${receipt.signature.key_id ?? "key"})` : "signature does NOT recover the attested receipt key \u2014 forged/altered";
2223
+ }
2224
+ } catch {
2225
+ sigDetail = "signature check errored";
2226
+ }
2227
+ }
2124
2228
  checks.push({
2125
2229
  name: "Receipt signature",
2126
- status: "skip",
2127
- detail: receipt.signature ? "present (local recompute via RedPill keyset canonicalization = roadmap)" : "no signature on receipt",
2128
- trust: "roadmap"
2230
+ status: sigStatus,
2231
+ detail: sigDetail,
2232
+ trust: sigStatus === "skip" ? "roadmap" : "trustless"
2129
2233
  });
2130
2234
  return {
2131
2235
  receiptId,
2132
- verified: Boolean(wireHash && workloadId) && anchorVerified,
2236
+ verified: Boolean(wireHash && workloadId) && anchorVerified && !signatureForged,
2133
2237
  anchorVerified,
2134
2238
  checks,
2135
2239
  wireHash,
2136
2240
  workloadId,
2137
2241
  upstream: upstreamEv ? {
2138
2242
  provider: upstreamEv.provider ?? upstreamEv.upstream_name,
2243
+ modelId: upstreamEv.model_id,
2139
2244
  result: upstreamEv.result,
2140
- tcbStatus: upstreamEv.tcb_status
2245
+ tcbStatus: upstreamEv.tcb_status,
2246
+ sessionId: upstreamEv.session_id,
2247
+ claims: claims.length > 0 ? claims : void 0
2141
2248
  } : void 0,
2142
2249
  anchor
2143
2250
  };