@brainai/satp-client 2.0.6 → 2.0.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.
@@ -144,6 +144,182 @@ function prepareIdentityAttestationRequest(opts = {}) {
144
144
  };
145
145
  }
146
146
 
147
+ function appendMismatch(errors, field, actual, expected) {
148
+ try {
149
+ if (canonicalStringify(actual) !== canonicalStringify(expected)) {
150
+ errors.push(`${field} does not match the value derived from public inputs`);
151
+ }
152
+ } catch (err) {
153
+ errors.push(`${field} must be canonical JSON-compatible data`);
154
+ }
155
+ }
156
+
157
+ function appendExpectation(errors, field, actual, expected, normalize) {
158
+ if (expected === undefined) return;
159
+
160
+ try {
161
+ const normalizedExpected = normalize ? normalize(expected) : expected;
162
+ if (actual !== normalizedExpected) {
163
+ errors.push(`${field} does not match the expected value`);
164
+ }
165
+ } catch (err) {
166
+ errors.push(`expected ${field}: ${err.message}`);
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Verify an offline identity-attestation request from public inputs only.
172
+ *
173
+ * The verifier recomputes the canonical request hash, program IDs, agent hash,
174
+ * and PDAs. It never connects to RPC, reads credentials, builds a transaction,
175
+ * signs, sends, or mutates chain state.
176
+ *
177
+ * @param {object} request
178
+ * @param {object} [expectations]
179
+ * @returns {{ok: boolean, errors: string[], warnings: string[]}}
180
+ */
181
+ function verifyIdentityAttestationRequest(request, expectations = {}) {
182
+ const errors = [];
183
+ const warnings = [];
184
+
185
+ if (!request || typeof request !== 'object' || Array.isArray(request)) {
186
+ return { ok: false, errors: ['request must be an object'], warnings };
187
+ }
188
+ if (!expectations || typeof expectations !== 'object' || Array.isArray(expectations)) {
189
+ return { ok: false, errors: ['expectations must be an object'], warnings };
190
+ }
191
+
192
+ if (request.schemaVersion !== REQUEST_SCHEMA_VERSION) {
193
+ errors.push(`schemaVersion must be ${REQUEST_SCHEMA_VERSION}`);
194
+ }
195
+ if (request.requestType !== 'identity-attestation') {
196
+ errors.push('requestType must be identity-attestation');
197
+ }
198
+ if (request.mode !== 'unsigned-readonly-request') {
199
+ errors.push('mode must be unsigned-readonly-request');
200
+ }
201
+ if (request.signingRequired !== false) {
202
+ errors.push('signingRequired must be false');
203
+ }
204
+ if (request.unsigned !== true) {
205
+ errors.push('unsigned must be true');
206
+ }
207
+ if (!Array.isArray(request.instructions) || request.instructions.length !== 0) {
208
+ errors.push('instructions must be an empty array');
209
+ }
210
+ if (!Array.isArray(request.signers) || request.signers.length !== 0) {
211
+ errors.push('signers must be an empty array');
212
+ }
213
+ if (request.transaction !== null) {
214
+ errors.push('transaction must be null');
215
+ }
216
+
217
+ const hashPayload = { ...request };
218
+ delete hashPayload.requestHash;
219
+ let recomputedRequestHash;
220
+ try {
221
+ recomputedRequestHash = hashObject(hashPayload);
222
+ } catch (err) {
223
+ errors.push('request payload must be canonical JSON-compatible data');
224
+ }
225
+ if (typeof request.requestHash !== 'string' || !/^[a-f0-9]{64}$/.test(request.requestHash)) {
226
+ errors.push('requestHash must be a lowercase 32-byte hex string');
227
+ } else if (recomputedRequestHash && request.requestHash !== recomputedRequestHash) {
228
+ errors.push('requestHash does not match the canonical request payload');
229
+ }
230
+
231
+ let expectedRequest;
232
+ try {
233
+ expectedRequest = prepareIdentityAttestationRequest({
234
+ subjectWallet: request.subjectWallet,
235
+ agentId: request.agentId,
236
+ claimType: request.claimType,
237
+ metadataHash: request.metadataHash,
238
+ attester: request.attester,
239
+ network: request.network,
240
+ expiresAt: request.expiresAt,
241
+ });
242
+ } catch (err) {
243
+ errors.push(`public inputs are invalid: ${err.message}`);
244
+ }
245
+
246
+ if (expectedRequest) {
247
+ for (const field of [
248
+ 'network',
249
+ 'subjectWallet',
250
+ 'agentId',
251
+ 'attester',
252
+ 'claimType',
253
+ 'attestationType',
254
+ 'metadataHash',
255
+ 'proofData',
256
+ 'expiresAt',
257
+ 'agentIdHash',
258
+ 'genesisPda',
259
+ 'genesisBump',
260
+ 'attestationPda',
261
+ 'attestationBump',
262
+ 'programs',
263
+ 'requestHash',
264
+ ]) {
265
+ appendMismatch(errors, field, request[field], expectedRequest[field]);
266
+ }
267
+ }
268
+
269
+ appendExpectation(
270
+ errors,
271
+ 'subjectWallet',
272
+ request.subjectWallet,
273
+ expectations.expectedSubjectWallet,
274
+ (value) => normalizePublicKey(value, 'expectedSubjectWallet'),
275
+ );
276
+ appendExpectation(
277
+ errors,
278
+ 'agentId',
279
+ request.agentId,
280
+ expectations.expectedAgentId,
281
+ (value) => normalizeString(value, 'expectedAgentId'),
282
+ );
283
+ appendExpectation(
284
+ errors,
285
+ 'claimType',
286
+ request.claimType,
287
+ expectations.expectedClaimType,
288
+ (value) => normalizeString(value, 'expectedClaimType', { maxBytes: 32 }),
289
+ );
290
+ appendExpectation(
291
+ errors,
292
+ 'metadataHash',
293
+ request.metadataHash,
294
+ expectations.expectedMetadataHash,
295
+ normalizeMetadataHash,
296
+ );
297
+ appendExpectation(
298
+ errors,
299
+ 'attester',
300
+ request.attester,
301
+ expectations.expectedAttester,
302
+ (value) => normalizePublicKey(value, 'expectedAttester'),
303
+ );
304
+ appendExpectation(
305
+ errors,
306
+ 'network',
307
+ request.network,
308
+ expectations.expectedNetwork,
309
+ normalizeNetwork,
310
+ );
311
+ appendExpectation(
312
+ errors,
313
+ 'expiresAt',
314
+ request.expiresAt,
315
+ expectations.expectedExpiresAt,
316
+ normalizeExpiresAt,
317
+ );
318
+
319
+ return { ok: errors.length === 0, errors, warnings };
320
+ }
321
+
147
322
  module.exports = {
148
323
  prepareIdentityAttestationRequest,
324
+ verifyIdentityAttestationRequest,
149
325
  };
package/src/index.d.ts CHANGED
@@ -46,6 +46,22 @@ export interface IdentityAttestationRequest {
46
46
  requestHash: string;
47
47
  }
48
48
 
49
+ export interface IdentityAttestationRequestVerificationOptions {
50
+ expectedSubjectWallet?: PublicKey | string;
51
+ expectedAgentId?: string;
52
+ expectedClaimType?: string;
53
+ expectedMetadataHash?: string;
54
+ expectedAttester?: PublicKey | string;
55
+ expectedNetwork?: Network;
56
+ expectedExpiresAt?: number | null;
57
+ }
58
+
59
+ export interface IdentityAttestationRequestVerification {
60
+ ok: boolean;
61
+ errors: string[];
62
+ warnings: string[];
63
+ }
64
+
49
65
  export interface SatpTrustPacket {
50
66
  schemaVersion: 'satp.trustPacket.v1';
51
67
  packetType: 'satp-trust-packet';
@@ -89,6 +105,138 @@ export interface SatpTrustPacketValidation {
89
105
  errors: string[];
90
106
  }
91
107
 
108
+ export const RUNTIME_AUTHORIZATION_EVIDENCE_SCHEMA_VERSION: 'satp.runtimeAuthorizationEvidence.v0';
109
+ export const RUNTIME_AUTHORIZATION_EVIDENCE_PROFILE_ID: 'satp.runtimeAuthorizationEvidence';
110
+ export const RUNTIME_AUTHORIZATION_EVIDENCE_PROFILE_VERSION: '0';
111
+ export const RUNTIME_AUTHORIZATION_EVIDENCE_REASON_CODES: Readonly<{
112
+ UNSUPPORTED_PROFILE: 'unsupported_profile';
113
+ INVALID_EVIDENCE: 'invalid_evidence';
114
+ AUTHORIZATION_EXPIRED: 'authorization_expired';
115
+ SCOPE_MISMATCH: 'scope_mismatch';
116
+ VERIFIER_UNAVAILABLE: 'verifier_unavailable';
117
+ }>;
118
+
119
+ export interface RuntimeAuthorizationEvidenceDigest {
120
+ algorithm: 'sha256';
121
+ value: string;
122
+ }
123
+
124
+ export interface RuntimeAuthorizationEvidence {
125
+ schemaVersion: 'satp.runtimeAuthorizationEvidence.v0';
126
+ profile: {
127
+ id: 'satp.runtimeAuthorizationEvidence';
128
+ version: '0';
129
+ };
130
+ issuer: string;
131
+ verifier: string;
132
+ subject: string;
133
+ audience: string;
134
+ resource: string;
135
+ evidenceDigest: RuntimeAuthorizationEvidenceDigest;
136
+ observedAt: string;
137
+ expiresAt: string;
138
+ authorizationScope: string[];
139
+ policyDigest: RuntimeAuthorizationEvidenceDigest;
140
+ }
141
+
142
+ export interface RuntimeAuthorizationEvidenceInput {
143
+ schemaVersion?: string;
144
+ schema_version?: string;
145
+ profile?: { id?: string; version?: string | number };
146
+ profileId?: string;
147
+ profile_id?: string;
148
+ profileVersion?: string | number;
149
+ profile_version?: string | number;
150
+ issuer?: string | { id: string };
151
+ issuerId?: string;
152
+ issuer_id?: string;
153
+ verifier?: string | { id: string };
154
+ verifierId?: string;
155
+ verifier_id?: string;
156
+ subject?: string | { id: string };
157
+ subjectId?: string;
158
+ subject_id?: string;
159
+ audience?: string | { id: string };
160
+ audienceId?: string;
161
+ audience_id?: string;
162
+ resource?: string;
163
+ evidenceDigest?: RuntimeAuthorizationEvidenceDigest | string;
164
+ evidence_digest?: RuntimeAuthorizationEvidenceDigest | string;
165
+ observedAt?: string | number | Date;
166
+ observed_at?: string | number | Date;
167
+ expiresAt?: string | number | Date;
168
+ expires_at?: string | number | Date;
169
+ authorizationScope?: string | string[];
170
+ authorization_scope?: string | string[];
171
+ policyDigest?: RuntimeAuthorizationEvidenceDigest | string;
172
+ policy_digest?: RuntimeAuthorizationEvidenceDigest | string;
173
+ evidence?: {
174
+ digest?: RuntimeAuthorizationEvidenceDigest | string;
175
+ observedAt?: string | number | Date;
176
+ observed_at?: string | number | Date;
177
+ };
178
+ authorization?: {
179
+ scope?: string | string[];
180
+ expiresAt?: string | number | Date;
181
+ expires_at?: string | number | Date;
182
+ };
183
+ policy?: { digest?: RuntimeAuthorizationEvidenceDigest | string };
184
+ }
185
+
186
+ export interface RuntimeAuthorizationEvidenceVerificationOptions {
187
+ now?: string | number | Date;
188
+ expectedProfileId?: string;
189
+ expectedProfileVersion?: string | number;
190
+ expectedIssuer?: string | { id: string };
191
+ expectedVerifier?: string | { id: string };
192
+ expectedSubject?: string | { id: string };
193
+ expectedAudience?: string | { id: string };
194
+ expectedResource?: string;
195
+ expectedEvidenceDigest?: RuntimeAuthorizationEvidenceDigest | string;
196
+ expectedPolicyDigest?: RuntimeAuthorizationEvidenceDigest | string;
197
+ requiredScope?: string | string[];
198
+ requiredScopes?: string | string[];
199
+ availableVerifiers?: string | string[] | Set<string> | Record<string, boolean>;
200
+ }
201
+
202
+ export type RuntimeAuthorizationEvidenceReasonCode =
203
+ | 'unsupported_profile'
204
+ | 'invalid_evidence'
205
+ | 'authorization_expired'
206
+ | 'scope_mismatch'
207
+ | 'verifier_unavailable';
208
+
209
+ export interface RuntimeAuthorizationEvidenceVerification {
210
+ ok: boolean;
211
+ reasonCode: RuntimeAuthorizationEvidenceReasonCode | null;
212
+ reasonCodes: RuntimeAuthorizationEvidenceReasonCode[];
213
+ message: string;
214
+ evidence: RuntimeAuthorizationEvidence | null;
215
+ checks: {
216
+ profileSupported: boolean;
217
+ structurallyValid: boolean;
218
+ authorizationCurrent?: boolean;
219
+ scopeMatches?: boolean;
220
+ verifierAvailable?: boolean;
221
+ };
222
+ guardrails: {
223
+ offlineOnly: true;
224
+ networkRequests: false;
225
+ writesSolanaState: false;
226
+ usesKeypairs: false;
227
+ authorizesPayment: false;
228
+ };
229
+ }
230
+
231
+ export function normalizeRuntimeAuthorizationEvidence(
232
+ input: RuntimeAuthorizationEvidenceInput
233
+ ): RuntimeAuthorizationEvidence;
234
+
235
+ export function verifyRuntimeAuthorizationEvidence(
236
+ input: RuntimeAuthorizationEvidenceInput,
237
+ options?: RuntimeAuthorizationEvidenceVerificationOptions
238
+ ): RuntimeAuthorizationEvidenceVerification;
239
+
92
240
  export interface X402PaymentRequirement {
93
241
  scheme?: string;
94
242
  network?: string;
@@ -140,7 +288,64 @@ export type RuntimePolicyDecision =
140
288
  | 'degrade'
141
289
  | 'needs_approval';
142
290
 
291
+ export interface RuntimePolicyActorEvidence {
292
+ verifier_id?: string;
293
+ verifierId?: string;
294
+ verified: boolean;
295
+ revoked?: boolean;
296
+ actor_id?: string;
297
+ actorId?: string;
298
+ subject_id?: string;
299
+ subjectId?: string;
300
+ issued_at?: string | number | Date;
301
+ issuedAt?: string | number | Date;
302
+ expires_at?: string | number | Date;
303
+ expiresAt?: string | number | Date;
304
+ delegation_depth?: number;
305
+ delegationDepth?: number;
306
+ delegation?: { depth?: number };
307
+ action_binding?: RuntimePolicyActionBinding;
308
+ actionBinding?: RuntimePolicyActionBinding;
309
+ }
310
+
311
+ export interface RuntimePolicyActionBinding {
312
+ action_id?: string;
313
+ actionId?: string;
314
+ type?: string;
315
+ resource?: string;
316
+ operation?: string;
317
+ }
318
+
319
+ export interface RuntimePolicyX402SettlementContext {
320
+ settlement_id?: string;
321
+ settlementId?: string;
322
+ verifier_id?: string;
323
+ verifierId?: string;
324
+ verified: boolean;
325
+ status: 'settled' | string;
326
+ purpose: 'action_payment' | 'evidence_lookup' | string;
327
+ actor_id?: string;
328
+ actorId?: string;
329
+ subject_id?: string;
330
+ subjectId?: string;
331
+ action_id?: string;
332
+ actionId?: string;
333
+ resource: string | null;
334
+ amount_usd?: number;
335
+ amountUsd?: number;
336
+ settled_at?: string | number | Date;
337
+ settledAt?: string | number | Date;
338
+ }
339
+
143
340
  export interface RuntimePolicyIdentityPayload {
341
+ subject_id?: string;
342
+ subjectId?: string;
343
+ actor_id?: string;
344
+ actorId?: string;
345
+ actor_evidence?: RuntimePolicyActorEvidence;
346
+ actorEvidence?: RuntimePolicyActorEvidence;
347
+ agentId?: string;
348
+ profileId?: string;
144
349
  active?: boolean;
145
350
  satpVerified?: boolean;
146
351
  verified?: boolean;
@@ -152,6 +357,8 @@ export interface RuntimePolicyIdentityPayload {
152
357
 
153
358
  export interface RuntimePolicyActionDescriptor {
154
359
  schemaVersion?: string;
360
+ action_id?: string;
361
+ actionId?: string;
155
362
  surface?: string;
156
363
  type?: string;
157
364
  resource?: string;
@@ -178,6 +385,7 @@ export interface RuntimePolicyActionDescriptorBuildInput extends RuntimePolicyAc
178
385
 
179
386
  export interface RuntimePolicyHostActionDescriptor extends RuntimePolicyActionDescriptor {
180
387
  schemaVersion: 'satp.runtimePolicyHostActionDescriptor.v1';
388
+ actionId: string | null;
181
389
  type: string;
182
390
  resource: string | null;
183
391
  operation: string | null;
@@ -204,6 +412,9 @@ export interface RuntimePolicyConfig {
204
412
  maxAutoSpendUsd?: number;
205
413
  requireVerifiedIdentity?: boolean;
206
414
  staleEvidenceAfterMs?: number;
415
+ maxActorEvidenceAgeMs?: number;
416
+ maxDelegationDepth?: number;
417
+ maxSettlementAgeMs?: number;
207
418
  }
208
419
 
209
420
  export interface RuntimePolicyOptions {
@@ -211,6 +422,8 @@ export interface RuntimePolicyOptions {
211
422
  actionPaymentPreapproved?: boolean;
212
423
  evidenceLookupPaymentPreapproved?: boolean;
213
424
  operatorApproved?: boolean;
425
+ x402_settlement?: RuntimePolicyX402SettlementContext;
426
+ x402Settlement?: RuntimePolicyX402SettlementContext;
214
427
  policy?: RuntimePolicyConfig;
215
428
  }
216
429
 
@@ -237,6 +450,9 @@ export interface RuntimePolicyAuditTrace {
237
450
  message: string;
238
451
  subject: {
239
452
  agentId: string | null;
453
+ subjectId: string | null;
454
+ actorId: string | null;
455
+ actorEvidencePresent: boolean;
240
456
  active: boolean;
241
457
  verified: boolean;
242
458
  trustScoreBand: '90-100' | '80-89' | '70-79' | '50-69' | '25-49' | '0-24';
@@ -609,6 +825,11 @@ export function prepareIdentityAttestationRequest(
609
825
  opts: IdentityAttestationRequestOptions
610
826
  ): IdentityAttestationRequest;
611
827
 
828
+ export function verifyIdentityAttestationRequest(
829
+ request: IdentityAttestationRequest | Record<string, unknown> | null | undefined,
830
+ expectations?: IdentityAttestationRequestVerificationOptions
831
+ ): IdentityAttestationRequestVerification;
832
+
612
833
  export const TRUST_PACKET_SCHEMA_VERSION: 'satp.trustPacket.v1';
613
834
 
614
835
  export function buildSatpTrustPacket(
package/src/index.js CHANGED
@@ -782,7 +782,10 @@ class SATPSDK {
782
782
  const v3sdk = require('./v3-sdk');
783
783
  const v3pda = require('./v3-pda');
784
784
  const v3Borsh = require('./borsh-reader');
785
- const { prepareIdentityAttestationRequest } = require('./attestation-request');
785
+ const {
786
+ prepareIdentityAttestationRequest,
787
+ verifyIdentityAttestationRequest,
788
+ } = require('./attestation-request');
786
789
  const {
787
790
  TRUST_PACKET_SCHEMA_VERSION,
788
791
  buildSatpTrustPacket,
@@ -800,6 +803,7 @@ const {
800
803
  evaluateRuntimePolicy,
801
804
  } = require('./runtime-policy-adapter');
802
805
  const walletControlChallenge = require('./wallet-control-challenge');
806
+ const runtimeAuthorizationEvidence = require('./runtime-authorization-evidence');
803
807
  const x402Discovery = require('./x402-discovery');
804
808
  const signerPolicy = require('./signer-policy');
805
809
 
@@ -884,6 +888,7 @@ module.exports = {
884
888
  getV3EscrowVaultATA: v3pda.getV3EscrowVaultATA,
885
889
  deriveReviewAttestationPda: getReviewAttestationPDA,
886
890
  prepareIdentityAttestationRequest,
891
+ verifyIdentityAttestationRequest,
887
892
  TRUST_PACKET_SCHEMA_VERSION,
888
893
  buildSatpTrustPacket,
889
894
  validateSatpTrustPacket,
@@ -896,6 +901,12 @@ module.exports = {
896
901
  hashWalletControlChallenge: walletControlChallenge.hashWalletControlChallenge,
897
902
  deriveWalletControlChallengePdas: walletControlChallenge.deriveWalletControlChallengePdas,
898
903
  verifyWalletControlChallengeSignature: walletControlChallenge.verifyWalletControlChallengeSignature,
904
+ RUNTIME_AUTHORIZATION_EVIDENCE_SCHEMA_VERSION: runtimeAuthorizationEvidence.RUNTIME_AUTHORIZATION_EVIDENCE_SCHEMA_VERSION,
905
+ RUNTIME_AUTHORIZATION_EVIDENCE_PROFILE_ID: runtimeAuthorizationEvidence.RUNTIME_AUTHORIZATION_EVIDENCE_PROFILE_ID,
906
+ RUNTIME_AUTHORIZATION_EVIDENCE_PROFILE_VERSION: runtimeAuthorizationEvidence.RUNTIME_AUTHORIZATION_EVIDENCE_PROFILE_VERSION,
907
+ RUNTIME_AUTHORIZATION_EVIDENCE_REASON_CODES: runtimeAuthorizationEvidence.RUNTIME_AUTHORIZATION_EVIDENCE_REASON_CODES,
908
+ normalizeRuntimeAuthorizationEvidence: runtimeAuthorizationEvidence.normalizeRuntimeAuthorizationEvidence,
909
+ verifyRuntimeAuthorizationEvidence: runtimeAuthorizationEvidence.verifyRuntimeAuthorizationEvidence,
899
910
  X402_PAYMENT_IS_NOT_ACTION_AUTHORIZATION: x402Discovery.X402_PAYMENT_IS_NOT_ACTION_AUTHORIZATION,
900
911
  X402_DISCOVERY_SCHEMA_VERSION: x402Discovery.X402_DISCOVERY_SCHEMA_VERSION,
901
912
  RUNTIME_POLICY_ACTION_DESCRIPTOR_SCHEMA_VERSION: x402Discovery.RUNTIME_POLICY_ACTION_DESCRIPTOR_SCHEMA_VERSION,