@chitmark/sdk 0.8.0 → 0.9.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/README.md +41 -172
- package/dist/edge.cjs +34 -8
- package/dist/edge.cjs.map +1 -1
- package/dist/edge.d.cts +2 -2
- package/dist/edge.d.ts +2 -2
- package/dist/edge.js +34 -8
- package/dist/edge.js.map +1 -1
- package/dist/index.cjs +34 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +34 -8
- package/dist/index.js.map +1 -1
- package/dist/{trusted-ip-BvSjdU80.d.cts → trusted-ip-DNO3xzCU.d.cts} +41 -9
- package/dist/{trusted-ip-BvSjdU80.d.ts → trusted-ip-DNO3xzCU.d.ts} +41 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,208 +1,77 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Chitmark SDK for TypeScript
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
- [Trust Decisions](https://chitmark.com/docs/quickstart/trust-decisions) lets you screen costly actions such as signups, credit grants, and API requests with `verify`, `feedback`, and `challenge`.
|
|
6
|
-
- [Agent Attribution](https://chitmark.com/docs/guides/agent-attribution) lets you recognize returning site sessions and evaluate signed proof from linked agents.
|
|
7
|
-
- [Agent Paid Access](https://chitmark.com/docs/guides/agent-paid-access) lets you buy or sell controlled access to API resources with hosted checkout and seller-controlled delivery.
|
|
3
|
+
Use Chitmark from your backend to screen costly actions, recognize enrolled agents with signed proof, and manage paid API access.
|
|
8
4
|
|
|
9
5
|
[](https://www.npmjs.com/package/@chitmark/sdk)
|
|
10
6
|
|
|
11
|
-
Try it without a key: [run the playground](https://chitmark.com/playground). Live service health: [chitmark.com/status](https://chitmark.com/status).
|
|
12
|
-
|
|
13
7
|
## Install
|
|
14
8
|
|
|
9
|
+
Requires Node.js 24 or later. The package ships TypeScript declarations and supports ESM and CommonJS.
|
|
10
|
+
|
|
15
11
|
```bash
|
|
16
12
|
npm install @chitmark/sdk
|
|
17
|
-
# or
|
|
18
|
-
pnpm add @chitmark/sdk
|
|
19
13
|
```
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## Trust Decisions quick start
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
import { Chitmark } from "@chitmark/sdk";
|
|
27
|
-
|
|
28
|
-
const chitmark = new Chitmark({
|
|
29
|
-
apiKey: process.env.CHITMARK_API_KEY!,
|
|
30
|
-
// Optional, defaults shown
|
|
31
|
-
onDegraded: "challenge", // timeouts return a challenge, they never throw or allow
|
|
32
|
-
timeoutMs: 800, // local base URLs default to 2000ms
|
|
33
|
-
piiMode: "hashed", // 'hashed' | 'none' | 'raw'
|
|
34
|
-
env: "production",
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
const verdict = await chitmark.verify({
|
|
38
|
-
action: "signup",
|
|
39
|
-
session: "sess_9f3a",
|
|
40
|
-
surface: "app.acme.com/signup",
|
|
41
|
-
subject: {
|
|
42
|
-
email: "buyer@acmecorp.com", // hashed client-side before the wire
|
|
43
|
-
ip: "203.0.113.7", // truncated to /24 client-side
|
|
44
|
-
userAgent: req.headers["user-agent"],
|
|
45
|
-
headers: req.headers,
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
if (verdict.decision === "challenge") {
|
|
50
|
-
// stop and run the challenge flow (below)
|
|
51
|
-
}
|
|
52
|
-
// Persist verdict.eventId on the account row: feedback joins only on that id.
|
|
53
|
-
```
|
|
15
|
+
Create a scoped API key in the [Chitmark dashboard](https://chitmark.com/dashboard) and set `CHITMARK_API_KEY` in your server environment.
|
|
54
16
|
|
|
55
|
-
##
|
|
17
|
+
## Resolve agent attribution
|
|
56
18
|
|
|
57
|
-
|
|
58
|
-
| :------------ | :--------------------- | :------------------- | :--------------------------- |
|
|
59
|
-
| **verify** | `chitmark.verify()` | `POST /v1/verify` | At signup / trial / action |
|
|
60
|
-
| **feedback** | `chitmark.feedback()` | `POST /v1/feedback` | After an outcome is known |
|
|
61
|
-
| **challenge** | `chitmark.challenge()` | `POST /v1/challenge` | On `challenge` or `degraded` |
|
|
19
|
+
Use a key with `attribution:read`. Replace the sample request with the inbound request from your server.
|
|
62
20
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
never returns HTTP 503. Timeouts and transport errors synthesize a local
|
|
66
|
-
degraded challenge Verdict and never throw on `verify`. HTTP 4xx / 429 Error
|
|
67
|
-
responses throw `ChitmarkApiError`. Set `onDegraded` to `challenge` (the only
|
|
68
|
-
supported value), never allow on degraded.
|
|
69
|
-
|
|
70
|
-
### Report outcomes
|
|
71
|
-
|
|
72
|
-
Store the `eventId` from verify on the account row, then report what happened against that same id. Never guess or derive the id from email or IP.
|
|
21
|
+
```ts
|
|
22
|
+
import { Chitmark } from "@chitmark/sdk";
|
|
73
23
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
24
|
+
const chitmark = new Chitmark({ apiKey: process.env.CHITMARK_API_KEY! });
|
|
25
|
+
const request = new Request("https://app.example.com/agent-task");
|
|
26
|
+
const result = await chitmark.attribution.resolve({ request });
|
|
77
27
|
|
|
78
|
-
|
|
79
|
-
await chitmark.feedback({
|
|
80
|
-
eventId: account.chitmarkEventId, // the stored join key
|
|
81
|
-
outcome: "credit_burn", // converted | chargeback | abuse_confirmed | ...
|
|
82
|
-
value: 87.4, // measured magnitude: `unit` rides along (default "usd"; also "credits" | "count")
|
|
83
|
-
observedAt: "2026-08-06T04:00:00Z",
|
|
84
|
-
});
|
|
28
|
+
console.log(result.identity.recognized, result.principal.email);
|
|
85
29
|
```
|
|
86
30
|
|
|
87
|
-
|
|
31
|
+
The SDK reads fresh signed evidence from `Chitmark-Agent-Proof`. Without evidence, the sample returns an unknown identity and null email. An enrolled agent can sign for different recipients in a network-enabled deployment. Each site receives its own subject, and principal claims require a current grant for the recipient and purpose. Email also requires consent, `attribution:email`, and raw mode on an eligible plan.
|
|
88
32
|
|
|
89
|
-
|
|
33
|
+
For relative request URLs, pass your configured HTTPS origin as `site`. See the [Attribution quickstart](https://chitmark.com/docs/guides/agent-attribution/) and [network proof guide](https://chitmark.com/docs/guides/agent-attribution-network/).
|
|
90
34
|
|
|
91
|
-
|
|
35
|
+
## Screen a signup
|
|
92
36
|
|
|
93
|
-
|
|
94
|
-
import { createHash } from "node:crypto";
|
|
37
|
+
Use a key with `verify:write`. Generate and retain a session ID on your server for the attempt.
|
|
95
38
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
if (issued.instructions.type === "pow") {
|
|
102
|
-
const { challengeId, instructions } = issued;
|
|
103
|
-
const prefix = "0".repeat(instructions.difficulty);
|
|
104
|
-
let nonce = 0;
|
|
105
|
-
for (; ; nonce++) {
|
|
106
|
-
const hex = createHash("sha256")
|
|
107
|
-
.update(`${challengeId}:${instructions.seed}:${nonce}`)
|
|
108
|
-
.digest("hex");
|
|
109
|
-
if (hex.startsWith(prefix)) break;
|
|
110
|
-
}
|
|
111
|
-
await chitmark.completeChallenge({
|
|
112
|
-
eventId: verdict.eventId,
|
|
113
|
-
challengeId,
|
|
114
|
-
session: "sess_9f3a",
|
|
115
|
-
proof: { type: "proof_of_work", nonce: String(nonce) },
|
|
116
|
-
});
|
|
117
|
-
// Re-verify with context: { challengeId }; completion alone does not authorize.
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### Verify the receipt
|
|
122
|
-
|
|
123
|
-
Every production verdict ships a `verdictToken`: an ES256 JWT bound to session, origin, and event. Verify authenticity, then authorize on the decision:
|
|
39
|
+
```ts
|
|
40
|
+
import { randomUUID } from "node:crypto";
|
|
41
|
+
import { Chitmark, isEligibleAllow } from "@chitmark/sdk";
|
|
124
42
|
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
43
|
+
const chitmark = new Chitmark({ apiKey: process.env.CHITMARK_API_KEY! });
|
|
44
|
+
const verdict = await chitmark.verify({
|
|
45
|
+
action: "signup",
|
|
46
|
+
session: randomUUID(),
|
|
47
|
+
surface: "app.example.com/signup",
|
|
48
|
+
subject: { email: "buyer@example.com" },
|
|
129
49
|
});
|
|
130
50
|
|
|
131
|
-
|
|
132
|
-
throw new ChallengeRequiredError(); // HTTP 428 / challenge response
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Only now trust the allow.
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Rejects expired tokens, unknown keys, bad signatures, and session or origin mismatches with typed `VerdictTokenError` codes. A verified token that is not a non-degraded `allow` must still fail closed.
|
|
139
|
-
|
|
140
|
-
Cache JWKS using HTTP headers: respect `Cache-Control` (`max-age=60`), and when `ETag` or `Last-Modified` are present use conditional requests (`If-None-Match` / `If-Modified-Since`) instead of a hard-coded refresh interval. Refresh immediately on unknown `kid`. Do not fetch on every verification. Every signed JWT requires `kid`. Keys are EC P-256 for ES256: require JWT `alg === "ES256"` before verifying (`wrong_algorithm` otherwise); never trust the token-requested alg. Rotation overlaps old and new keys; keep the retiring key until tokens under it expire (≥ 5 minutes + 60 seconds). An empty JWKS (`keys: []`) means verification is impossible: fail closed; never treat an empty set as proof a token is valid (local/CI may also emit `unsigned.<eventId>`). Pass a cached `jwks` option into `verifyVerdictToken` to skip the network call.
|
|
141
|
-
|
|
142
|
-
### PII modes
|
|
143
|
-
|
|
144
|
-
| Mode | Behavior |
|
|
145
|
-
| :----------------- | :--------------------------------------------------------- |
|
|
146
|
-
| `hashed` (default) | SHA-256 email, /24 IP truncation, allowlisted form fields |
|
|
147
|
-
| `none` | Derived/header-shape signals only (Web Bot Auth preserved) |
|
|
148
|
-
| `raw` | Tenant opt-in only; triggers higher compliance review |
|
|
149
|
-
|
|
150
|
-
## Framework integrations
|
|
151
|
-
|
|
152
|
-
Express middleware and a Next.js route helper ship in the package. They normalize transient failures, challenge responses (HTTP 428), and deny (HTTP 403) for you. Handle raw status codes when using the low-level `Chitmark` client or curl.
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
// Express: 403 on deny, 428 + challenge payload on challenge
|
|
156
|
-
// 428 means the request is not rejected permanently; a required verification
|
|
157
|
-
// precondition must be satisfied before the action can proceed.
|
|
158
|
-
import { chitmarkGate } from "@chitmark/sdk";
|
|
159
|
-
app.post("/signup", chitmarkGate(chitmark, "signup"), signupHandler);
|
|
160
|
-
|
|
161
|
-
// Next.js App Router
|
|
162
|
-
import { verifyNextRequest } from "@chitmark/sdk";
|
|
163
|
-
const verdict = await verifyNextRequest(req, chitmark, { action: "signup" });
|
|
51
|
+
console.log(verdict.decision, isEligibleAllow(verdict), verdict.eventId);
|
|
164
52
|
```
|
|
165
53
|
|
|
166
|
-
|
|
54
|
+
Grant value only after an explicit, non-degraded allow. Timeouts and transport failures on `verify` produce a degraded challenge. The default data mode hashes email before sending it. Store the returned `eventId` for outcome reporting.
|
|
167
55
|
|
|
168
|
-
|
|
56
|
+
| Method | Use |
|
|
57
|
+
| --------------------------------- | --------------------------------------------------------------------------- |
|
|
58
|
+
| `verify` | Get an allow, challenge, or deny verdict. |
|
|
59
|
+
| `challenge` / `completeChallenge` | Issue and complete an additional check. Reuse the attempt's session. |
|
|
60
|
+
| `feedback` | Report an outcome against the stored `eventId` with a `feedback:write` key. |
|
|
61
|
+
| `verifyVerdictToken` | Verify a signed receipt against the expected session and tenant. |
|
|
169
62
|
|
|
170
|
-
|
|
171
|
-
import { Chitmark } from "@chitmark/sdk/edge";
|
|
172
|
-
|
|
173
|
-
const chitmark = new Chitmark({ apiKey: env.CHITMARK_API_KEY });
|
|
174
|
-
const verdict = await chitmark.verifyRequest(req, { action: "signup" });
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
## Agent Attribution
|
|
178
|
-
|
|
179
|
-
Use an Attribution read key to recognize site sessions or resolve an authorized principal association. Unknown is a successful outcome, and email is nullable.
|
|
180
|
-
|
|
181
|
-
```ts
|
|
182
|
-
const result = await chitmark.attribution.resolve({ request });
|
|
183
|
-
console.log(result.identity.recognized, result.principal.email);
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
See the [Agent Attribution guide](https://chitmark.com/docs/guides/agent-attribution/) for request adapters, link grants, and disclosure rules.
|
|
63
|
+
See the [Trust Decisions quickstart](https://chitmark.com/docs/quickstart/trust-decisions/), [challenge flow](https://chitmark.com/docs/api/challenge/), and [verdict token guide](https://chitmark.com/docs/guides/verdict-tokens/).
|
|
187
64
|
|
|
188
65
|
## Agent Paid Access
|
|
189
66
|
|
|
190
|
-
|
|
67
|
+
Use `chitmark.charge(request)` to request a quote, continue checkout, or read purchase status. Sellers use separate scopes to reserve execution and record delivery. See the [Agent Paid Access guide](https://chitmark.com/docs/guides/agent-paid-access/).
|
|
191
68
|
|
|
192
|
-
##
|
|
193
|
-
|
|
194
|
-
Using Cursor, Claude Code, Codex, or another coding agent? Point it at
|
|
195
|
-
[chitmark.com/SKILL.md](https://chitmark.com/SKILL.md), or paste this into your
|
|
196
|
-
prompt: `Integrate Chitmark into my app following https://chitmark.com/SKILL.md`.
|
|
197
|
-
|
|
198
|
-
## Resources
|
|
69
|
+
## Framework integrations
|
|
199
70
|
|
|
200
|
-
- [
|
|
201
|
-
- [
|
|
202
|
-
- [
|
|
203
|
-
- [OpenAPI contract](https://chitmark.com/openapi.yaml)
|
|
204
|
-
- [Examples repository](https://github.com/nonameuserd/chitmark-examples)
|
|
71
|
+
- Express and Next.js use `chitmarkGate` and `verifyNextRequest`. [Framework guide](https://chitmark.com/docs/guides/drop-ins/)
|
|
72
|
+
- Workers use `ChitmarkEdge` from `@chitmark/sdk/edge`. [TypeScript reference](https://chitmark.com/docs/sdk/typescript/)
|
|
73
|
+
- Try a verdict without an API key in the [playground](https://chitmark.com/playground/).
|
|
205
74
|
|
|
206
75
|
## License
|
|
207
76
|
|
|
208
|
-
Proprietary
|
|
77
|
+
Proprietary. The package includes the license terms in `LICENSE`.
|
package/dist/edge.cjs
CHANGED
|
@@ -2,21 +2,47 @@
|
|
|
2
2
|
|
|
3
3
|
// src/attribution.ts
|
|
4
4
|
function attributionEvidence(options, mode) {
|
|
5
|
-
const { request, agentProof, session, piiMode = mode } = options;
|
|
5
|
+
const { request, agentProof, session, purpose, piiMode = mode } = options;
|
|
6
6
|
const headers = request.headers;
|
|
7
7
|
const header = typeof headers.get === "function" ? headers.get("chitmark-agent-proof") : Object.entries(headers).find(
|
|
8
8
|
([name]) => name.toLowerCase() === "chitmark-agent-proof"
|
|
9
9
|
)?.[1];
|
|
10
|
-
let
|
|
11
|
-
if (!site && request.url
|
|
12
|
-
let proof = agentProof;
|
|
13
|
-
if (!proof && typeof header === "string" && header.length <= 2048 && site) {
|
|
10
|
+
let url;
|
|
11
|
+
if (!options.site && request.url) {
|
|
14
12
|
try {
|
|
15
|
-
|
|
13
|
+
url = new URL(request.url);
|
|
16
14
|
} catch {
|
|
17
15
|
}
|
|
18
16
|
}
|
|
19
|
-
|
|
17
|
+
const site = options.site ?? (url?.protocol === "https:" ? url.origin : void 0);
|
|
18
|
+
const proof = agentProof ?? (site ? parseProof(header) : void 0);
|
|
19
|
+
return { site, session, piiMode, purpose, agentProof: proof };
|
|
20
|
+
}
|
|
21
|
+
function parseProof(header) {
|
|
22
|
+
if (typeof header !== "string" || header.length > 2048) return;
|
|
23
|
+
try {
|
|
24
|
+
const value = JSON.parse(header);
|
|
25
|
+
if (!value || typeof value.nonce !== "string" || value.nonce.length > 36 || typeof value.issuedAt !== "number" || !Number.isSafeInteger(value.issuedAt) || typeof value.signature !== "string" || value.signature.length > 86)
|
|
26
|
+
return;
|
|
27
|
+
const { nonce, issuedAt, signature } = value;
|
|
28
|
+
if ("iss" in value) {
|
|
29
|
+
const key = value.agentKey;
|
|
30
|
+
if (value.iss === "chitmark-agent-key-v1" && key?.kty === "OKP" && key.crv === "Ed25519" && typeof key.x === "string" && key.x.length === 43 && !("d" in key)) {
|
|
31
|
+
return {
|
|
32
|
+
iss: value.iss,
|
|
33
|
+
agentKey: { kty: key.kty, crv: key.crv, x: key.x },
|
|
34
|
+
nonce,
|
|
35
|
+
issuedAt,
|
|
36
|
+
signature
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (typeof value.linkId === "string" && value.linkId.length <= 36)
|
|
42
|
+
return { linkId: value.linkId, nonce, issuedAt, signature };
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
return void 0;
|
|
20
46
|
}
|
|
21
47
|
|
|
22
48
|
// src/credentials.ts
|
|
@@ -153,7 +179,7 @@ var DEFAULT_BASE_URLS = {
|
|
|
153
179
|
};
|
|
154
180
|
var DEFAULT_TIMEOUT_MS = 800;
|
|
155
181
|
var LOCAL_TIMEOUT_MS = 2e3;
|
|
156
|
-
var SDK_VERSION = "0.
|
|
182
|
+
var SDK_VERSION = "0.9.0";
|
|
157
183
|
function defaultTimeoutMs(baseUrl) {
|
|
158
184
|
try {
|
|
159
185
|
const host = new URL(baseUrl).hostname.replace(/^\[|\]$/g, "").toLowerCase();
|