@chitmark/sdk 0.6.2 → 0.6.4
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/README.md +25 -11
- package/dist/edge.cjs +70 -21
- package/dist/edge.cjs.map +1 -1
- package/dist/edge.d.cts +3 -2
- package/dist/edge.d.ts +3 -2
- package/dist/edge.js +68 -22
- package/dist/edge.js.map +1 -1
- package/dist/index.cjs +73 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -5
- package/dist/index.d.ts +14 -5
- package/dist/index.js +71 -22
- package/dist/index.js.map +1 -1
- package/dist/trusted-ip-Ngrp6dUP.d.cts +608 -0
- package/dist/trusted-ip-Ngrp6dUP.d.ts +608 -0
- package/package.json +1 -1
- package/dist/http-status-BHYL507g.d.cts +0 -435
- package/dist/http-status-BHYL507g.d.ts +0 -435
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared API types for Chitmark by Open Agent Ledger.
|
|
3
|
+
* Aligns with openapi/openapi.yaml and SDK.md.
|
|
4
|
+
*/
|
|
5
|
+
/** Routing decision from verify. When degraded, this is always "challenge". */
|
|
6
|
+
type Decision = "allow" | "challenge" | "deny";
|
|
7
|
+
/** Classified actor behind the inbound action. */
|
|
8
|
+
type ActorType = "human" | "agent_principal" | "agent_abusive" | "unknown";
|
|
9
|
+
/** Whether a returned verdict is meant to gate traffic or only be observed. */
|
|
10
|
+
type EnforcementMode = "enforce" | "log_only";
|
|
11
|
+
/** Known action surfaces; custom strings are also accepted. */
|
|
12
|
+
type Action = "signup" | "trial_activation" | "api_key" | "demo_request" | "quote" | "ticket" | "custom" | (string & {});
|
|
13
|
+
/**
|
|
14
|
+
* Outcomes reported via feedback. Prefer fast labels first
|
|
15
|
+
* (`credit_burn`, `multi_account_cluster`, `abuse_confirmed`).
|
|
16
|
+
*
|
|
17
|
+
* `false_positive`: a previously attributed **abuse signal** on this
|
|
18
|
+
* `eventId` was reviewed and determined **not** to represent abuse
|
|
19
|
+
* (T&S clear / overturn). Conflicts with abuse-positive labels.
|
|
20
|
+
* Not "Chitmark challenged/denied a legitimate user" — that is
|
|
21
|
+
* `context.decisionCosts.falseChallenge` / `falseDeny` on verify.
|
|
22
|
+
*/
|
|
23
|
+
type Outcome = "credit_burn" | "multi_account_cluster" | "abuse_confirmed" | "converted" | "chargeback" | "churned" | "false_positive";
|
|
24
|
+
/** Asymmetric-cost challenge methods (best → worst for anti-abuse).
|
|
25
|
+
* `payment_preauth` validates a payment-provider setup result; it does not
|
|
26
|
+
* transfer funds or authorize a purchase. */
|
|
27
|
+
type ChallengeMethod = "payment_preauth" | "proof_of_work" | "device_attestation" | "web_bot_auth_stepup" | "email_otp";
|
|
28
|
+
/** Client-side PII minimization mode. Default is hashed. Raw is opt-in only. */
|
|
29
|
+
type PiiMode = "raw" | "hashed" | "none";
|
|
30
|
+
/** Environment target; selects default base URL. */
|
|
31
|
+
type ChitmarkEnv = "production" | "staging" | "development";
|
|
32
|
+
/** On timeout/error, client decision fallback. NEVER "allow". */
|
|
33
|
+
type OnDegraded = "challenge";
|
|
34
|
+
/**
|
|
35
|
+
* Principal attribution on a Verdict: who is acting and on whose behalf.
|
|
36
|
+
*
|
|
37
|
+
* Not an authorization credential. `resolved === true` is **not** "this is
|
|
38
|
+
* a human", not an IdP login, and not permission to unlock an action. Gate
|
|
39
|
+
* on `decision` (plus `degraded` / `enforcementMode`).
|
|
40
|
+
*
|
|
41
|
+
* - `resolved`: usable principal signal present (today: verified Web Bot
|
|
42
|
+
* Auth operator signature and/or enrichment known-good). Sparse/false for
|
|
43
|
+
* farms and unsigned traffic.
|
|
44
|
+
* - `operator`: bot maker from Web Bot Auth Signature-Agent (e.g. "openai")
|
|
45
|
+
* when the operator channel verified; does not by itself prove `onBehalfOf`.
|
|
46
|
+
* - `onBehalfOf`: principal the agent is acting for when known: organization
|
|
47
|
+
* **or** end-user identifier, depending on what the signing/context path
|
|
48
|
+
* supplies. Often absent at the wedge. Not sole authz.
|
|
49
|
+
* - `identityBasis`: signal sources that contributed (e.g. `web_bot_auth`,
|
|
50
|
+
* `email_domain`, `enrichment`); mixed cryptographic and inferred strength.
|
|
51
|
+
*/
|
|
52
|
+
interface Principal {
|
|
53
|
+
resolved: boolean;
|
|
54
|
+
/** Bot maker from Web Bot Auth Signature-Agent (e.g. "openai"). */
|
|
55
|
+
operator?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Principal the agent is acting for when known (org or end-user id).
|
|
58
|
+
* Often absent at the wedge; not sole authorization.
|
|
59
|
+
*/
|
|
60
|
+
onBehalfOf?: string;
|
|
61
|
+
identityBasis: string[];
|
|
62
|
+
}
|
|
63
|
+
/** Verdict object: single source of truth for a verify call. */
|
|
64
|
+
interface Verdict {
|
|
65
|
+
/** When degraded === true, this is ALWAYS "challenge", never "allow". */
|
|
66
|
+
decision: Decision;
|
|
67
|
+
actorType: ActorType;
|
|
68
|
+
/**
|
|
69
|
+
* Model confidence for the returned decision (0..1). Not a calibrated
|
|
70
|
+
* probability of abuse, and not the threshold `decisionCosts` moves. Gate
|
|
71
|
+
* on `decision` (plus degraded / enforcementMode); use for ops triage and
|
|
72
|
+
* dashboards, not as a substitute allow/deny cutoff.
|
|
73
|
+
*/
|
|
74
|
+
confidence: number;
|
|
75
|
+
/**
|
|
76
|
+
* Human-readable signal strings for ops (always populated). Display copy
|
|
77
|
+
* today (`string[]`); do not scrape text as a stable machine API. A future
|
|
78
|
+
* additive revision may expose `{code, message, category}` while keeping
|
|
79
|
+
* a human-readable message for analytics without string scraping.
|
|
80
|
+
*/
|
|
81
|
+
reasons: string[];
|
|
82
|
+
principal: Principal;
|
|
83
|
+
/** ES256 JWT decision receipt, bound to session.
|
|
84
|
+
* Under `enforcementMode: "log_only"` this is an observation receipt only:
|
|
85
|
+
* signature still verifies, but it does not authorize the action. */
|
|
86
|
+
verdictToken: string;
|
|
87
|
+
/** Store on the account row: feedback join key. */
|
|
88
|
+
eventId: string;
|
|
89
|
+
/** true → served under timeout/error → decision forced to challenge. */
|
|
90
|
+
degraded: boolean;
|
|
91
|
+
/** Server-computed mode. `log_only` → observe (token/decision non-authoritative for gating);
|
|
92
|
+
* `enforce` → partner may challenge / deny / allow. Not a JWT claim today. */
|
|
93
|
+
enforcementMode: EnforcementMode;
|
|
94
|
+
/**
|
|
95
|
+
* true → this verify honored `context.challengeId` for the session.
|
|
96
|
+
* Guaranteed only with `decision: "allow"` and `degraded: false` (clear
|
|
97
|
+
* short-circuits scoring). Under `log_only`, observation only.
|
|
98
|
+
*/
|
|
99
|
+
challengeCleared?: boolean;
|
|
100
|
+
}
|
|
101
|
+
/** Subject signals for verify. Prefer hashed fields over raw PII. */
|
|
102
|
+
interface Subject {
|
|
103
|
+
email?: string;
|
|
104
|
+
emailSha256?: string;
|
|
105
|
+
ip?: string;
|
|
106
|
+
ipTrunc?: string;
|
|
107
|
+
userAgent?: string;
|
|
108
|
+
/** Includes Web Bot Auth headers when present. */
|
|
109
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
110
|
+
formData?: Record<string, unknown>;
|
|
111
|
+
formDataHashed?: Record<string, unknown>;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Relative, dimensionless weights for three error classes (not dollars and
|
|
115
|
+
* not score thresholds; only ratios matter). Repositions allow/challenge/deny
|
|
116
|
+
* boundaries for a single verify.
|
|
117
|
+
*
|
|
118
|
+
* - `falseAllow`: cost of allowing abusive traffic
|
|
119
|
+
* - `falseChallenge`: cost of challenging legitimate traffic when allow
|
|
120
|
+
* would have been fine (a challenge can still be appropriate for
|
|
121
|
+
* legitimate users; this names the over-friction case)
|
|
122
|
+
* - `falseDeny`: cost of denying legitimate traffic
|
|
123
|
+
*
|
|
124
|
+
* Higher `falseAllow` challenges earlier (abuse-averse); higher
|
|
125
|
+
* `falseChallenge` lets more of the gray zone through; higher `falseDeny`
|
|
126
|
+
* reserves deny for confirmed abuse. Defaults 1:1:3 keep fail-to-challenge
|
|
127
|
+
* semantics. Missing/non-positive fields fall back to their default.
|
|
128
|
+
*/
|
|
129
|
+
interface DecisionCosts {
|
|
130
|
+
falseAllow?: number;
|
|
131
|
+
falseChallenge?: number;
|
|
132
|
+
falseDeny?: number;
|
|
133
|
+
}
|
|
134
|
+
/** Per-request policy flags for POST /v1/verify. */
|
|
135
|
+
interface VerifyContext {
|
|
136
|
+
/** Reference a challenge completed for this session; honored once. */
|
|
137
|
+
challengeId?: string;
|
|
138
|
+
/** "default" challenges the gray zone; "confirmed_tail_only" allows it in enforce mode. */
|
|
139
|
+
challengePolicy?: "default" | "confirmed_tail_only";
|
|
140
|
+
/** True → observation-only `log_only` verdict: nothing is blocked or challenged.
|
|
141
|
+
* Integration-side control only: set in your server code (or via env/key policy).
|
|
142
|
+
* Never derive from client-provided request data. */
|
|
143
|
+
logOnly?: boolean;
|
|
144
|
+
/** Relative dimensionless weights (falseAllow / falseChallenge / falseDeny); defaults 1:1:3. */
|
|
145
|
+
decisionCosts?: DecisionCosts;
|
|
146
|
+
/** Partner-specific flags (e.g. plan, referrer), carried, not scored. */
|
|
147
|
+
[key: string]: unknown;
|
|
148
|
+
}
|
|
149
|
+
/** Request body for POST /v1/verify. */
|
|
150
|
+
interface VerifyRequest {
|
|
151
|
+
idempotencyKey?: string;
|
|
152
|
+
action: Action;
|
|
153
|
+
/** Binding id you supply for this action attempt. Prefer high-entropy,
|
|
154
|
+
* server-generated ids; binds challenge and verdictToken (`sub`). */
|
|
155
|
+
session?: string;
|
|
156
|
+
surface?: string;
|
|
157
|
+
subject: Subject;
|
|
158
|
+
context?: VerifyContext;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Scale for feedback `value`. Allowlist only (not free-form currency codes).
|
|
162
|
+
* Values in different units are not converted or comparable. Ledger dollar
|
|
163
|
+
* totals sum `credit_burn` only when `unit` is `"usd"`.
|
|
164
|
+
*/
|
|
165
|
+
type FeedbackUnit = "usd" | "credits" | "count";
|
|
166
|
+
/**
|
|
167
|
+
* Provenance of a feedback label: why you trusted this outcome.
|
|
168
|
+
* `eventId` + `outcome` say what was learned; `source` says where it came from.
|
|
169
|
+
*/
|
|
170
|
+
type FeedbackSource = "billing" | "usage_metering" | "trust_queue" | "manual_review" | "customer_system" | "automated_detector";
|
|
171
|
+
/** Request body for POST /v1/feedback. */
|
|
172
|
+
interface FeedbackRequest {
|
|
173
|
+
/**
|
|
174
|
+
* Join key from a prior verify. Feedback cannot create attribution:
|
|
175
|
+
* unknown/cross-tenant ids return 404. Never invent a join from email/IP.
|
|
176
|
+
*/
|
|
177
|
+
eventId: string;
|
|
178
|
+
outcome: Outcome;
|
|
179
|
+
/**
|
|
180
|
+
* Measured economic magnitude for this outcome, expressed in `unit`.
|
|
181
|
+
* Optional. Not intrinsically dollars: pair with `unit` (`usd` |
|
|
182
|
+
* `credits` | `count`). Different units are not comparable.
|
|
183
|
+
* For `converted`, when set: realized first-year ARR at conversion
|
|
184
|
+
* (not expected ARR, multi-year TCV, or LTV).
|
|
185
|
+
*/
|
|
186
|
+
value?: number;
|
|
187
|
+
/** Scale for `value`. Default `usd` when `value` is set. Requires `value`. */
|
|
188
|
+
unit?: FeedbackUnit;
|
|
189
|
+
/**
|
|
190
|
+
* Provenance of the label. Optional. Included in warehouse identity when
|
|
191
|
+
* set (two sources with otherwise identical bodies are separate observations).
|
|
192
|
+
*/
|
|
193
|
+
source?: FeedbackSource;
|
|
194
|
+
observedAt?: string;
|
|
195
|
+
/** Body form of Idempotency-Key; header takes precedence. */
|
|
196
|
+
idempotencyKey?: string;
|
|
197
|
+
}
|
|
198
|
+
/** Response from POST /v1/feedback. */
|
|
199
|
+
interface FeedbackResponse {
|
|
200
|
+
ok: true;
|
|
201
|
+
eventId: string;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Scales the proof-of-work difficulty issued with a challenge so the
|
|
205
|
+
* challenge's economic tax rises with risk. Absent → "default".
|
|
206
|
+
* Monotonicity: riskTier ↑ ⇒ challenge cost never ↓
|
|
207
|
+
* (default → D, elevated → ≥ D, severe → ≥ elevated). Issued difficulty is
|
|
208
|
+
* sealed at issue and never re-derived from a later tier on complete.
|
|
209
|
+
*/
|
|
210
|
+
type ChallengeRiskTier = "default" | "elevated" | "severe";
|
|
211
|
+
/** Request body for POST /v1/challenge: issue or complete. */
|
|
212
|
+
interface ChallengeRequest {
|
|
213
|
+
/** eventId from the verify verdict that routed to challenge. */
|
|
214
|
+
eventId: string;
|
|
215
|
+
/** Binding id you supply for one action attempt. Prefer high-entropy,
|
|
216
|
+
* server-generated ids. Required for issue and completion (max 128 chars). */
|
|
217
|
+
session: string;
|
|
218
|
+
/**
|
|
219
|
+
* Ordered method hints, not guarantees. First issuable method wins;
|
|
220
|
+
* unsupported entries are skipped. Floor is proof_of_work; never email_otp.
|
|
221
|
+
*/
|
|
222
|
+
prefer?: ChallengeMethod[];
|
|
223
|
+
/** Completion mode: the challenge being solved. */
|
|
224
|
+
challengeId?: string;
|
|
225
|
+
/** Completion mode: server-verified proof bound to challengeId + session. */
|
|
226
|
+
proof?: ChallengeProof;
|
|
227
|
+
/**
|
|
228
|
+
* Issue-mode context. `riskTier` scales issued PoW difficulty monotonically
|
|
229
|
+
* (higher tier ⇒ cost never lower). Difficulty is sealed at issue;
|
|
230
|
+
* completion validates that value only.
|
|
231
|
+
*/
|
|
232
|
+
context?: {
|
|
233
|
+
riskTier?: ChallengeRiskTier;
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/** Server-verified completion proof. */
|
|
237
|
+
type ChallengeProof = {
|
|
238
|
+
type: "proof_of_work";
|
|
239
|
+
/** First nonce where sha256(`${challengeId}:${seed}:${nonce}`) hex starts with `difficulty` zero nibbles. */
|
|
240
|
+
nonce: string;
|
|
241
|
+
} | {
|
|
242
|
+
type: "stripe_setup_intent";
|
|
243
|
+
/** SetupIntent id from issued instructions; provider status must be succeeded. Validates result only: no charge, no purchase auth. */
|
|
244
|
+
setupIntentId: string;
|
|
245
|
+
};
|
|
246
|
+
/** Response from POST /v1/challenge (issue mode). */
|
|
247
|
+
interface ChallengeResponse {
|
|
248
|
+
challengeId: string;
|
|
249
|
+
method: ChallengeMethod;
|
|
250
|
+
instructions: {
|
|
251
|
+
type: "pow";
|
|
252
|
+
/** Leading zero nibbles required. Sealed at issue; completion validates this value. */
|
|
253
|
+
difficulty: number;
|
|
254
|
+
/** Combine as `${challengeId}:${seed}:${nonce}`. */
|
|
255
|
+
seed: string;
|
|
256
|
+
/** Only supported PoW algorithm today. */
|
|
257
|
+
algorithm: "sha256-prefix";
|
|
258
|
+
} | {
|
|
259
|
+
type: "stripe_setup_intent";
|
|
260
|
+
/** test = real Stripe test-mode; stub = deterministic local/CI id. */
|
|
261
|
+
mode: "test" | "stub";
|
|
262
|
+
clientSecret: string;
|
|
263
|
+
publishableKey?: string;
|
|
264
|
+
};
|
|
265
|
+
/** Solving only clears THIS session (anti-replay). Never a global identity/account/IP/device clear. */
|
|
266
|
+
boundTo: string;
|
|
267
|
+
expiresAt: string;
|
|
268
|
+
}
|
|
269
|
+
/** Response from POST /v1/challenge (completion mode).
|
|
270
|
+
* Session-state change only: does not authorize the action. Re-verify with
|
|
271
|
+
* `context.challengeId` and proceed only on non-degraded allow. */
|
|
272
|
+
interface ChallengeCompletionResponse {
|
|
273
|
+
/** Friction satisfied for boundTo; not permission to continue without re-verify. */
|
|
274
|
+
ok: true;
|
|
275
|
+
challengeId: string;
|
|
276
|
+
/** Session that was cleared (single use). Never a global identity/account/IP/device clear. */
|
|
277
|
+
boundTo: string;
|
|
278
|
+
clearedAt: string;
|
|
279
|
+
}
|
|
280
|
+
/** Client constructor options. */
|
|
281
|
+
interface ChitmarkOptions {
|
|
282
|
+
/** API key (Bearer). Use process.env.CHITMARK_API_KEY. */
|
|
283
|
+
apiKey: string;
|
|
284
|
+
/**
|
|
285
|
+
* Timeout/error behavior. Fixed to "challenge": never allow silent bypass.
|
|
286
|
+
* @default "challenge"
|
|
287
|
+
*/
|
|
288
|
+
onDegraded?: OnDegraded;
|
|
289
|
+
/**
|
|
290
|
+
* Client-side request budget in ms. Edge path is far faster.
|
|
291
|
+
* @default 800
|
|
292
|
+
*/
|
|
293
|
+
timeoutMs?: number;
|
|
294
|
+
/**
|
|
295
|
+
* Client-side PII minimization before send.
|
|
296
|
+
* @default "hashed"
|
|
297
|
+
*/
|
|
298
|
+
piiMode?: PiiMode;
|
|
299
|
+
/**
|
|
300
|
+
* Selects default base URL unless baseUrl or CHITMARK_BASE_URL is set.
|
|
301
|
+
* @default "production"
|
|
302
|
+
*/
|
|
303
|
+
env?: ChitmarkEnv;
|
|
304
|
+
/**
|
|
305
|
+
* Override API host (e.g. local edge). Precedence:
|
|
306
|
+
* baseUrl > CHITMARK_BASE_URL env > env default.
|
|
307
|
+
*/
|
|
308
|
+
baseUrl?: string;
|
|
309
|
+
/** Custom fetch implementation (Workers, tests). */
|
|
310
|
+
fetch?: typeof fetch;
|
|
311
|
+
/** User-Agent / client identification sent as X-Chitmark-Client. */
|
|
312
|
+
clientName?: string;
|
|
313
|
+
}
|
|
314
|
+
interface ChitmarkErrorBody {
|
|
315
|
+
error: string;
|
|
316
|
+
message?: string;
|
|
317
|
+
code?: string;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Base error for all Chitmark client failures.
|
|
322
|
+
*/
|
|
323
|
+
declare class ChitmarkError extends Error {
|
|
324
|
+
name: string;
|
|
325
|
+
readonly status?: number;
|
|
326
|
+
readonly code?: string;
|
|
327
|
+
readonly body?: ChitmarkErrorBody | string;
|
|
328
|
+
constructor(message: string, options?: {
|
|
329
|
+
status?: number;
|
|
330
|
+
code?: string;
|
|
331
|
+
body?: ChitmarkErrorBody | string;
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Raised when the API returns a non-2xx response.
|
|
336
|
+
*/
|
|
337
|
+
declare class ChitmarkApiError extends ChitmarkError {
|
|
338
|
+
name: string;
|
|
339
|
+
constructor(status: number, body: ChitmarkErrorBody | string, message?: string);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Build the degraded challenge verdict used on timeout / transport failure.
|
|
343
|
+
* Golden rule: fail to challenge, never to allow.
|
|
344
|
+
*/
|
|
345
|
+
declare function degradedChallengeVerdict(reason: string, session?: string): Verdict;
|
|
346
|
+
/**
|
|
347
|
+
* Ensure degraded responses never surface as allow (defense in depth client-side).
|
|
348
|
+
*/
|
|
349
|
+
declare function enforceDegradedNeverAllow(verdict: Verdict): Verdict;
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Client-side verdictToken verification.
|
|
353
|
+
*
|
|
354
|
+
* A verdictToken is an ES256 JWT decision receipt minted by the edge and bound
|
|
355
|
+
* to session (`sub`), origin hint (`aud`), issuing tenant (`tid`), and a unique
|
|
356
|
+
* `jti` (correlation id) with a short `exp` (5 minutes). Every signed token
|
|
357
|
+
* requires a `kid` in the JWT header. Signature verification proves
|
|
358
|
+
* authenticity and integrity; it does not make the JWT single-use. This module
|
|
359
|
+
* always enforces `session` and `tenantId` (security boundary), validates
|
|
360
|
+
* `iss` against the Chitmark issuer, and optionally `aud`. `aud` reflects
|
|
361
|
+
* request headers and is attacker influenced: never enforce `aud` alone.
|
|
362
|
+
* Rejection reasons are surfaced as `VerdictTokenError.code`.
|
|
363
|
+
*
|
|
364
|
+
* JWKS: respect `Cache-Control` and, when present, `ETag` /
|
|
365
|
+
* `Last-Modified` via conditional requests (instead of a hard-coded
|
|
366
|
+
* refresh interval). Refresh immediately on unknown `kid`. Do not fetch on
|
|
367
|
+
* every verification; never permanently pin a single signing key. Each JWKS
|
|
368
|
+
* key is EC P-256 for ES256: require JWT `alg === "ES256"` before verifying
|
|
369
|
+
* (`wrong_algorithm` otherwise); never trust the token-requested algorithm.
|
|
370
|
+
* Rotation overlaps old and new keys; keep the retiring key until tokens under it
|
|
371
|
+
* expire (≥ 5 minutes + 60 seconds). Pass a cached `jwks` option to skip
|
|
372
|
+
* the network call. An empty JWKS (`keys: []`) means no token can be
|
|
373
|
+
* cryptographically verified: fail closed; never treat an empty set as
|
|
374
|
+
* evidence that a token is valid (local/CI may emit `unsigned.<eventId>`).
|
|
375
|
+
*/
|
|
376
|
+
|
|
377
|
+
/** Canonical issuer claim on every signed verdictToken. */
|
|
378
|
+
declare const VERDICT_TOKEN_ISSUER = "https://api.chitmark.com";
|
|
379
|
+
interface VerdictTokenClaims {
|
|
380
|
+
/** Issuer: https://api.chitmark.com. */
|
|
381
|
+
iss: string;
|
|
382
|
+
eventId: string;
|
|
383
|
+
decision: Decision;
|
|
384
|
+
actorType: ActorType;
|
|
385
|
+
confidence: number;
|
|
386
|
+
aud: string;
|
|
387
|
+
sub: string;
|
|
388
|
+
/** Issuing tenant, server-derived from the API key used at verify time. */
|
|
389
|
+
tid: string;
|
|
390
|
+
/** Unique token identifier (correlation). Not single-use unless you track it. */
|
|
391
|
+
jti: string;
|
|
392
|
+
iat: number;
|
|
393
|
+
exp: number;
|
|
394
|
+
degraded: boolean;
|
|
395
|
+
}
|
|
396
|
+
type VerdictTokenErrorCode = "malformed_token" | "wrong_algorithm" | "wrong_type" | "jwks_unavailable" | "unknown_kid" | "invalid_signature" | "expired" | "not_yet_valid" | "wrong_issuer" | "wrong_session" | "wrong_aud" | "wrong_tenant";
|
|
397
|
+
declare class VerdictTokenError extends ChitmarkError {
|
|
398
|
+
name: string;
|
|
399
|
+
constructor(code: VerdictTokenErrorCode, message: string);
|
|
400
|
+
}
|
|
401
|
+
interface VerifyVerdictTokenOptions {
|
|
402
|
+
/** Expected session: token `sub` must equal this or verification fails. */
|
|
403
|
+
session: string;
|
|
404
|
+
/** Expected issuing tenant: token `tid` must equal this or verification fails. */
|
|
405
|
+
tenantId: string;
|
|
406
|
+
/** Expected origin hint: token `aud` must equal this or verification fails. */
|
|
407
|
+
aud?: string;
|
|
408
|
+
/**
|
|
409
|
+
* Expected issuer (`iss`). Defaults to {@link VERDICT_TOKEN_ISSUER}.
|
|
410
|
+
* Override only in tests.
|
|
411
|
+
*/
|
|
412
|
+
issuer?: string;
|
|
413
|
+
/** JWKS URL; defaults to https://api.chitmark.com/.well-known/jwks.json. */
|
|
414
|
+
jwksUrl?: string;
|
|
415
|
+
/**
|
|
416
|
+
* Pre-fetched / cached JWKS; skips the network fetch when provided.
|
|
417
|
+
* Prefer caching via HTTP headers (`Cache-Control`, `ETag`, and
|
|
418
|
+
* `Last-Modified` when present) with conditional refresh and immediate
|
|
419
|
+
* refresh on unknown `kid`, rather than fetching on every verification.
|
|
420
|
+
*/
|
|
421
|
+
jwks?: {
|
|
422
|
+
keys: Array<Record<string, unknown>>;
|
|
423
|
+
};
|
|
424
|
+
/** Injectable fetch (Node 24+/browsers have a global). */
|
|
425
|
+
fetch?: typeof fetch;
|
|
426
|
+
/** Clock override for tests. */
|
|
427
|
+
nowMs?: number;
|
|
428
|
+
/** Clock skew tolerance in seconds; defaults to 30. */
|
|
429
|
+
leewaySeconds?: number;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Verify a verdictToken signature and binding.
|
|
433
|
+
*
|
|
434
|
+
* `session` and `tenantId` are required: they are the security boundary for
|
|
435
|
+
* cross-service authorization, not optional hints.
|
|
436
|
+
*/
|
|
437
|
+
declare function verifyVerdictToken(token: string, options: VerifyVerdictTokenOptions): Promise<VerdictTokenClaims>;
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Official Chitmark client (Open Agent Ledger): three verbs:
|
|
441
|
+
* verify, feedback, challenge. Trust decisions on agent-mediated actions,
|
|
442
|
+
* tuned by business outcomes.
|
|
443
|
+
*
|
|
444
|
+
* @example
|
|
445
|
+
* ```ts
|
|
446
|
+
* const chitmark = new Chitmark({ apiKey: process.env.CHITMARK_API_KEY! });
|
|
447
|
+
* const verdict = await chitmark.verify({
|
|
448
|
+
* action: "signup",
|
|
449
|
+
* subject: { email, ip, userAgent },
|
|
450
|
+
* });
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
declare class Chitmark {
|
|
454
|
+
private readonly apiKey;
|
|
455
|
+
private readonly baseUrl;
|
|
456
|
+
private readonly timeoutMs;
|
|
457
|
+
private readonly piiMode;
|
|
458
|
+
private readonly onDegraded;
|
|
459
|
+
private readonly fetchImpl;
|
|
460
|
+
private readonly clientName;
|
|
461
|
+
constructor(options: ChitmarkOptions);
|
|
462
|
+
/**
|
|
463
|
+
* Score + route an inbound action (signup / trial / …).
|
|
464
|
+
* Client budget defaults to 800ms (2000ms for local base URLs).
|
|
465
|
+
*
|
|
466
|
+
* Store-outage degradation arrives as **HTTP 200** with a Verdict body
|
|
467
|
+
* (`degraded: true`, `decision: "challenge"`). The SDK deserializes that
|
|
468
|
+
* like any other Verdict. Verify never returns HTTP 503.
|
|
469
|
+
*
|
|
470
|
+
* HTTP 4xx / 429 Error responses throw `ChitmarkApiError`. Transport
|
|
471
|
+
* timeout / network failure (or a malformed 200 body) synthesizes a local
|
|
472
|
+
* degraded challenge Verdict (never allow, never crashes).
|
|
473
|
+
*
|
|
474
|
+
* Persist `v.eventId` on the account row: feedback joins only on that id.
|
|
475
|
+
*/
|
|
476
|
+
verify(request: VerifyRequest): Promise<Verdict>;
|
|
477
|
+
/**
|
|
478
|
+
* Report a downstream outcome joined on the stored eventId from verify.
|
|
479
|
+
*
|
|
480
|
+
* Security invariant: feedback cannot create attribution. It only attaches
|
|
481
|
+
* an outcome to an existing verify event for this tenant. Unknown or
|
|
482
|
+
* cross-tenant eventIds return 404. Never invent a join from email/IP.
|
|
483
|
+
*
|
|
484
|
+
* Append-only (not latest-wins): multiple outcomes may attach to one
|
|
485
|
+
* eventId. Exact duplicates share one feedbackId. Compatible labels
|
|
486
|
+
* coexist; conflicting pairs quarantine the new observation without
|
|
487
|
+
* deleting prior evidence. HTTP `{ ok: true }` still acknowledges
|
|
488
|
+
* quarantined ingest.
|
|
489
|
+
*
|
|
490
|
+
* `idempotencyKey` is sent as both the Idempotency-Key header and body field
|
|
491
|
+
* (header takes precedence server-side) so exact retries replay the original
|
|
492
|
+
* acknowledgement.
|
|
493
|
+
*/
|
|
494
|
+
feedback(request: FeedbackRequest): Promise<FeedbackResponse>;
|
|
495
|
+
/**
|
|
496
|
+
* Issue an asymmetric-cost challenge after decision === challenge | degraded.
|
|
497
|
+
*/
|
|
498
|
+
challenge(request: ChallengeRequest): Promise<ChallengeResponse>;
|
|
499
|
+
/**
|
|
500
|
+
* Complete a challenge by submitting its solved proof. Clears only the bound
|
|
501
|
+
* session, once. Does not authorize the action: re-verify with
|
|
502
|
+
* `context.challengeId` and proceed only on non-degraded allow.
|
|
503
|
+
*/
|
|
504
|
+
completeChallenge(request: {
|
|
505
|
+
eventId: string;
|
|
506
|
+
challengeId: string;
|
|
507
|
+
session: string;
|
|
508
|
+
proof: ChallengeProof;
|
|
509
|
+
}): Promise<ChallengeCompletionResponse>;
|
|
510
|
+
/**
|
|
511
|
+
* Verify a verdictToken's signature against the API's JWKS and its session /
|
|
512
|
+
* tenant binding. Prefers a cached `jwks` (refresh periodically and on
|
|
513
|
+
* unknown `kid`); do not fetch on every call. Rejects expired tokens,
|
|
514
|
+
* unknown kids, and invalid signatures with `VerdictTokenError`. Always
|
|
515
|
+
* pass `session` and `tenantId` (required security boundary); `aud`
|
|
516
|
+
* optionally enforces the origin hint only.
|
|
517
|
+
*/
|
|
518
|
+
verifyVerdictToken(token: string, options: Omit<VerifyVerdictTokenOptions, "jwksUrl" | "fetch">): Promise<VerdictTokenClaims>;
|
|
519
|
+
/**
|
|
520
|
+
* Low-level HTTP helper. Prefer verify / feedback / challenge.
|
|
521
|
+
*/
|
|
522
|
+
request<T>(path: string, init: {
|
|
523
|
+
method: string;
|
|
524
|
+
body?: unknown;
|
|
525
|
+
idempotencyKey?: string;
|
|
526
|
+
}): Promise<T>;
|
|
527
|
+
/** Public accessors for inspection / tests. */
|
|
528
|
+
get config(): {
|
|
529
|
+
baseUrl: string;
|
|
530
|
+
timeoutMs: number;
|
|
531
|
+
piiMode: PiiMode;
|
|
532
|
+
onDegraded: OnDegraded;
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Client-side PII minimization helpers.
|
|
538
|
+
* Default SDK mode hashes email and truncates IP before leaving the customer process.
|
|
539
|
+
*/
|
|
540
|
+
/**
|
|
541
|
+
* SHA-256 hex digest of lowercased, trimmed email.
|
|
542
|
+
* Uses Web Crypto (Node 18+, browsers, Workers).
|
|
543
|
+
*/
|
|
544
|
+
declare function hashEmailSha256(email: string): Promise<string>;
|
|
545
|
+
/**
|
|
546
|
+
* Truncate IPv4 to /24 or IPv6 to /48 for coarse geo without host identity.
|
|
547
|
+
*/
|
|
548
|
+
declare function truncateIp(ip: string): string;
|
|
549
|
+
/**
|
|
550
|
+
* Reduce form payload to non-PII keys (domain-style fields only for hashed mode).
|
|
551
|
+
* Free-text is dropped; companyDomain-style keys are kept as-is when domain-like.
|
|
552
|
+
*/
|
|
553
|
+
declare function minimizeFormData(formData: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
|
|
554
|
+
|
|
555
|
+
declare const CHITMARK_HTTP_STATUS: {
|
|
556
|
+
readonly ok: 200;
|
|
557
|
+
readonly created: 201;
|
|
558
|
+
readonly badRequest: 400;
|
|
559
|
+
readonly unauthorized: 401;
|
|
560
|
+
readonly forbidden: 403;
|
|
561
|
+
readonly notFound: 404;
|
|
562
|
+
readonly conflict: 409;
|
|
563
|
+
readonly preconditionRequired: 428;
|
|
564
|
+
readonly tooManyRequests: 429;
|
|
565
|
+
};
|
|
566
|
+
type ChitmarkHttpStatus = (typeof CHITMARK_HTTP_STATUS)[keyof typeof CHITMARK_HTTP_STATUS];
|
|
567
|
+
declare const CHITMARK_HTTP_STATUS_MEANING: {
|
|
568
|
+
readonly ok: "Request succeeded. For verify gates, continue only after an explicit non-degraded allow.";
|
|
569
|
+
readonly created: "Resource was created after an explicit non-degraded allow.";
|
|
570
|
+
readonly badRequest: "Required request fields are missing or malformed.";
|
|
571
|
+
readonly unauthorized: "API key is missing or invalid.";
|
|
572
|
+
readonly forbidden: "Verify returned deny; block the action.";
|
|
573
|
+
readonly notFound: "Referenced Chitmark resource was not found.";
|
|
574
|
+
readonly conflict: "Idempotency key is already associated with a different payload fingerprint.";
|
|
575
|
+
readonly preconditionRequired: "428 means the request is not rejected permanently; a required verification precondition must be satisfied before the action can proceed.";
|
|
576
|
+
readonly tooManyRequests: "Request was rate limited.";
|
|
577
|
+
};
|
|
578
|
+
/**
|
|
579
|
+
* Explicit non-degraded allow. Use this (or `isEligibleAllow`) before any
|
|
580
|
+
* protected action so unknown / malformed decisions cannot fall through.
|
|
581
|
+
*/
|
|
582
|
+
type EligibleAllowVerdict = Verdict & {
|
|
583
|
+
decision: "allow";
|
|
584
|
+
degraded: false;
|
|
585
|
+
};
|
|
586
|
+
/**
|
|
587
|
+
* Type guard for the only verdict that may continue a gated action.
|
|
588
|
+
* Fail closed: anything that is not an explicit non-degraded allow is ineligible.
|
|
589
|
+
*/
|
|
590
|
+
declare function isEligibleAllow(verdict: Verdict): verdict is EligibleAllowVerdict;
|
|
591
|
+
/**
|
|
592
|
+
* Map a verify verdict to an HTTP status for framework drop-ins.
|
|
593
|
+
* Unknown or malformed decisions fail to challenge (428), never to allow (200).
|
|
594
|
+
*/
|
|
595
|
+
declare function verdictToHttpStatus(verdict: Verdict): ChitmarkHttpStatus;
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Resolve a client IP for verify subject signals from a Fetch Request.
|
|
599
|
+
*
|
|
600
|
+
* Prefer platform-set headers (`cf-connecting-ip`, `x-real-ip`,
|
|
601
|
+
* `x-vercel-forwarded-for`). `X-Forwarded-For` is only safe when a trusted
|
|
602
|
+
* proxy boundary strips or rewrites client-supplied values. Do not read XFF
|
|
603
|
+
* on a directly reachable origin. On Cloudflare Workers, prefer
|
|
604
|
+
* `verifyRequest` from `@chitmark/sdk/edge`, which owns this lookup.
|
|
605
|
+
*/
|
|
606
|
+
declare function getTrustedClientIp(req: Request): string;
|
|
607
|
+
|
|
608
|
+
export { type Action as A, VerdictTokenError as B, Chitmark as C, type Decision as D, type EligibleAllowVerdict as E, type FeedbackRequest as F, type VerdictTokenErrorCode as G, type VerifyVerdictTokenOptions as H, degradedChallengeVerdict as I, enforceDegradedNeverAllow as J, getTrustedClientIp as K, hashEmailSha256 as L, isEligibleAllow as M, minimizeFormData as N, type OnDegraded as O, type PiiMode as P, truncateIp as Q, verdictToHttpStatus as R, type Subject as S, verifyVerdictToken as T, type VerifyContext as V, type VerifyRequest as a, type Verdict as b, type ActorType as c, CHITMARK_HTTP_STATUS as d, CHITMARK_HTTP_STATUS_MEANING as e, type ChallengeCompletionResponse as f, type ChallengeMethod as g, type ChallengeProof as h, type ChallengeRequest as i, type ChallengeResponse as j, type ChallengeRiskTier as k, ChitmarkApiError as l, type ChitmarkEnv as m, ChitmarkError as n, type ChitmarkErrorBody as o, type ChitmarkHttpStatus as p, type ChitmarkOptions as q, type DecisionCosts as r, type EnforcementMode as s, type FeedbackResponse as t, type FeedbackSource as u, type FeedbackUnit as v, type Outcome as w, type Principal as x, VERDICT_TOKEN_ISSUER as y, type VerdictTokenClaims as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chitmark/sdk",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "Official TypeScript SDK for Chitmark by Open Agent Ledger: trust decisions on agent-mediated actions (verify, feedback, challenge)",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Open Agent Ledger",
|