@hai.ai/jacs 0.6.0 → 0.8.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 +336 -52
- package/client.d.ts +96 -0
- package/client.js +560 -0
- package/express.d.ts +69 -0
- package/express.js +130 -0
- package/express.js.map +1 -0
- package/index.d.ts +117 -96
- package/index.js +19 -17
- package/jacs.darwin-arm64.node +0 -0
- package/jacs.darwin-x64.node +0 -0
- package/jacs.linux-arm-gnueabihf.node +0 -0
- package/jacs.linux-arm-musleabihf.node +0 -0
- package/jacs.linux-arm64-gnu.node +0 -0
- package/jacs.linux-x64-gnu.node +0 -0
- package/jacs.linux-x64-musl.node +0 -0
- package/koa.d.ts +59 -0
- package/koa.js +124 -0
- package/koa.js.map +1 -0
- package/langchain.d.ts +97 -0
- package/langchain.js +439 -0
- package/langchain.js.map +1 -0
- package/mcp.d.ts +75 -42
- package/mcp.js +449 -422
- package/mcp.js.map +1 -1
- package/package.json +91 -7
- package/scripts/install-cli.js +125 -0
- package/simple.d.ts +92 -430
- package/simple.js +507 -524
- package/src/a2a.js +2 -2
- package/testing.d.ts +39 -0
- package/testing.js +49 -0
- package/vercel-ai.d.ts +54 -0
- package/vercel-ai.js +162 -0
- package/vercel-ai.js.map +1 -0
- package/mcp.ts +0 -521
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# JACS for Node.js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Sign it. Prove it.**
|
|
4
|
+
|
|
5
|
+
Cryptographic signatures for AI agent outputs -- so anyone can verify who said what and whether it was changed. No server. Three lines of code. Optionally register with [HAI.ai](https://hai.ai) for cross-organization key discovery.
|
|
6
|
+
|
|
7
|
+
[Which integration should I use?](https://humanassisted.github.io/JACS/getting-started/decision-tree.html) | [Full documentation](https://humanassisted.github.io/JACS/)
|
|
4
8
|
|
|
5
9
|
**Dependencies**: The `overrides` in `package.json` for `body-parser` and `qs` are for security (CVE-2024-45590). Do not remove them without re-auditing.
|
|
6
10
|
|
|
@@ -12,53 +16,91 @@ npm install @hai.ai/jacs
|
|
|
12
16
|
|
|
13
17
|
The npm package ships prebuilt native bindings for supported targets and does not compile Rust during `npm install`.
|
|
14
18
|
|
|
19
|
+
## v0.8.0: Framework Adapters
|
|
20
|
+
|
|
21
|
+
New in v0.8.0: first-class adapters for **Vercel AI SDK**, **Express**, **Koa**, **LangChain.js**, and a full **MCP tool suite**. All framework dependencies are optional peer deps — install only what you use.
|
|
22
|
+
|
|
23
|
+
### Async-First API
|
|
24
|
+
|
|
25
|
+
All NAPI operations return Promises by default. Sync variants are available with a `Sync` suffix, following the Node.js convention (like `fs.readFile` vs `fs.readFileSync`).
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
// Async (default, recommended -- does not block the event loop)
|
|
29
|
+
const signed = await jacs.signMessage({ action: 'approve' });
|
|
30
|
+
|
|
31
|
+
// Sync (blocks event loop, use in scripts or CLI tools)
|
|
32
|
+
const signed = jacs.signMessageSync({ action: 'approve' });
|
|
33
|
+
```
|
|
34
|
+
|
|
15
35
|
## Quick Start
|
|
16
36
|
|
|
37
|
+
Zero-config -- one call to start signing:
|
|
38
|
+
|
|
17
39
|
```javascript
|
|
18
40
|
const jacs = require('@hai.ai/jacs/simple');
|
|
19
41
|
|
|
20
|
-
|
|
21
|
-
const
|
|
42
|
+
await jacs.quickstart();
|
|
43
|
+
const signed = await jacs.signMessage({ action: 'approve', amount: 100 });
|
|
44
|
+
const result = await jacs.verify(signed.raw);
|
|
45
|
+
console.log(`Valid: ${result.valid}, Signer: ${result.signerId}`);
|
|
46
|
+
```
|
|
22
47
|
|
|
23
|
-
|
|
24
|
-
const signed = jacs.signMessage({
|
|
25
|
-
action: 'approve',
|
|
26
|
-
amount: 100
|
|
27
|
-
});
|
|
48
|
+
`quickstart()` creates a persistent agent with keys on disk. If `./jacs.config.json` already exists, it loads it; otherwise it creates a new agent. Agent, keys, and config are saved to `./jacs_data`, `./jacs_keys`, and `./jacs.config.json`. If `JACS_PRIVATE_KEY_PASSWORD` is not set, a secure password is auto-generated and saved to `./jacs_keys/.jacs_password`. Pass `{ algorithm: 'ring-Ed25519' }` to override the default (`pq2025`).
|
|
28
49
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
**Signed your first document?** Next: [Verify it standalone](#standalone-verification-no-agent-required) | [Add framework adapters](#framework-adapters) | [Multi-agent agreements](#multi-party-agreements) | [Full docs](https://humanassisted.github.io/JACS/getting-started/quick-start.html)
|
|
51
|
+
|
|
52
|
+
### Advanced: Loading an existing agent
|
|
53
|
+
|
|
54
|
+
If you already have an agent (e.g., created by a previous `quickstart()` call), load it explicitly:
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
const jacs = require('@hai.ai/jacs/simple');
|
|
58
|
+
|
|
59
|
+
await jacs.load('./jacs.config.json');
|
|
60
|
+
|
|
61
|
+
const signed = await jacs.signMessage({ action: 'approve', amount: 100 });
|
|
62
|
+
const result = await jacs.verify(signed.raw);
|
|
63
|
+
console.log(`Valid: ${result.valid}, Signer: ${result.signerId}`);
|
|
33
64
|
```
|
|
34
65
|
|
|
35
66
|
## Core API
|
|
36
67
|
|
|
68
|
+
Every function that calls into NAPI has both async (default) and sync variants:
|
|
69
|
+
|
|
70
|
+
| Function | Sync Variant | Description |
|
|
71
|
+
|----------|-------------|-------------|
|
|
72
|
+
| `quickstart(options?)` | `quickstartSync(options?)` | Create a persistent agent with keys on disk |
|
|
73
|
+
| `create(options)` | `createSync(options)` | Create a new agent programmatically |
|
|
74
|
+
| `load(configPath)` | `loadSync(configPath)` | Load agent from config file |
|
|
75
|
+
| `verifySelf()` | `verifySelfSync()` | Verify agent's own integrity |
|
|
76
|
+
| `updateAgent(data)` | `updateAgentSync(data)` | Update agent document |
|
|
77
|
+
| `updateDocument(id, data)` | `updateDocumentSync(id, data)` | Update existing document |
|
|
78
|
+
| `signMessage(data)` | `signMessageSync(data)` | Sign any JSON data |
|
|
79
|
+
| `signFile(path, embed)` | `signFileSync(path, embed)` | Sign a file |
|
|
80
|
+
| `verify(doc)` | `verifySync(doc)` | Verify signed document |
|
|
81
|
+
| `verifyById(id)` | `verifyByIdSync(id)` | Verify by storage ID |
|
|
82
|
+
| `reencryptKey(old, new)` | `reencryptKeySync(old, new)` | Re-encrypt private key |
|
|
83
|
+
| `getSetupInstructions(domain)` | `getSetupInstructionsSync(domain)` | Get DNS/well-known setup |
|
|
84
|
+
| `createAgreement(doc, ids, ...)` | `createAgreementSync(doc, ids, ...)` | Create multi-party agreement |
|
|
85
|
+
| `signAgreement(doc)` | `signAgreementSync(doc)` | Sign an agreement |
|
|
86
|
+
| `checkAgreement(doc)` | `checkAgreementSync(doc)` | Check agreement status |
|
|
87
|
+
| `audit(options?)` | `auditSync(options?)` | Run a security audit |
|
|
88
|
+
|
|
89
|
+
Pure sync functions (no NAPI call, no suffix needed):
|
|
90
|
+
|
|
37
91
|
| Function | Description |
|
|
38
92
|
|----------|-------------|
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `verifySelf()` | Verify agent's own integrity |
|
|
42
|
-
| `updateAgent(data)` | Update agent document with new data |
|
|
43
|
-
| `updateDocument(id, data)` | Update existing document with new data |
|
|
44
|
-
| `signMessage(data)` | Sign any JSON data |
|
|
45
|
-
| `signFile(path, embed)` | Sign a file |
|
|
46
|
-
| `verify(doc)` | Verify signed document (JSON string) |
|
|
47
|
-
| `verifyStandalone(doc, opts?)` | Verify without loading an agent (one-off) |
|
|
48
|
-
| `verifyById(id)` | Verify a document by storage ID (`uuid:version`) |
|
|
49
|
-
| `registerWithHai(opts?)` | Register the loaded agent with HAI.ai |
|
|
50
|
-
| `getDnsRecord(domain, ttl?)` | Get DNS TXT record line for the agent |
|
|
51
|
-
| `getWellKnownJson()` | Get well-known JSON for `/.well-known/jacs-pubkey.json` |
|
|
52
|
-
| `reencryptKey(oldPw, newPw)` | Re-encrypt private key with new password |
|
|
53
|
-
| `getPublicKey()` | Get public key for sharing |
|
|
93
|
+
| `verifyStandalone(doc, opts?)` | Verify without loading an agent |
|
|
94
|
+
| `getPublicKey()` | Get public key |
|
|
54
95
|
| `isLoaded()` | Check if agent is loaded |
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
96
|
+
| `getDnsRecord(domain, ttl?)` | Get DNS TXT record |
|
|
97
|
+
| `getWellKnownJson()` | Get well-known JSON |
|
|
98
|
+
| `trustAgent(json)` | Add agent to trust store |
|
|
99
|
+
| `listTrustedAgents()` | List trusted agent IDs |
|
|
100
|
+
| `untrustAgent(id)` | Remove from trust store |
|
|
101
|
+
| `isTrusted(id)` | Check if agent is trusted |
|
|
102
|
+
| `getTrustedAgent(id)` | Get trusted agent's JSON |
|
|
103
|
+
| `generateVerifyLink(doc, baseUrl?)` | Generate verification URL |
|
|
62
104
|
|
|
63
105
|
## Types
|
|
64
106
|
|
|
@@ -85,7 +127,7 @@ interface VerificationResult {
|
|
|
85
127
|
```typescript
|
|
86
128
|
const jacs = require('@hai.ai/jacs/simple');
|
|
87
129
|
|
|
88
|
-
const agent = jacs.create({
|
|
130
|
+
const agent = await jacs.create({
|
|
89
131
|
name: 'my-agent',
|
|
90
132
|
password: process.env.JACS_PRIVATE_KEY_PASSWORD, // required
|
|
91
133
|
algorithm: 'pq2025', // default; also: "ring-Ed25519", "RSA-PSS"
|
|
@@ -95,17 +137,39 @@ const agent = jacs.create({
|
|
|
95
137
|
console.log(`Created: ${agent.agentId}`);
|
|
96
138
|
```
|
|
97
139
|
|
|
140
|
+
### Standalone Verification (No Agent Required)
|
|
141
|
+
|
|
142
|
+
Verify a signed document without loading an agent. Useful for one-off verification, CI/CD pipelines, or services that only need to verify, not sign.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { verifyStandalone, generateVerifyLink } from '@hai.ai/jacs/simple';
|
|
146
|
+
|
|
147
|
+
const result = verifyStandalone(signedJson, {
|
|
148
|
+
keyResolution: 'local',
|
|
149
|
+
keyDirectory: './trusted-keys/',
|
|
150
|
+
});
|
|
151
|
+
if (result.valid) {
|
|
152
|
+
console.log(`Signed by: ${result.signerId}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Generate a shareable verification link
|
|
156
|
+
const url = generateVerifyLink(signed.raw);
|
|
157
|
+
// https://hai.ai/jacs/verify?s=<base64url-encoded-document>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Documents signed by Rust or Python agents verify identically in Node.js -- cross-language interop is tested on every commit with Ed25519 and pq2025 (ML-DSA-87). See the full [Verification Guide](https://humanassisted.github.io/JACS/getting-started/verification.html) for CLI, DNS, and cross-language examples.
|
|
161
|
+
|
|
98
162
|
### Verify by Document ID
|
|
99
163
|
|
|
100
164
|
```javascript
|
|
101
|
-
const result = jacs.verifyById('550e8400-e29b-41d4-a716-446655440000:1');
|
|
165
|
+
const result = await jacs.verifyById('550e8400-e29b-41d4-a716-446655440000:1');
|
|
102
166
|
console.log(`Valid: ${result.valid}`);
|
|
103
167
|
```
|
|
104
168
|
|
|
105
169
|
### Re-encrypt Private Key
|
|
106
170
|
|
|
107
171
|
```javascript
|
|
108
|
-
jacs.reencryptKey('old-password-123!', 'new-Str0ng-P@ss!');
|
|
172
|
+
await jacs.reencryptKey('old-password-123!', 'new-Str0ng-P@ss!');
|
|
109
173
|
```
|
|
110
174
|
|
|
111
175
|
### Password Requirements
|
|
@@ -123,17 +187,17 @@ The `pq-dilithium` algorithm is deprecated. Use `pq2025` (ML-DSA-87, FIPS-204) i
|
|
|
123
187
|
```javascript
|
|
124
188
|
const jacs = require('@hai.ai/jacs/simple');
|
|
125
189
|
|
|
126
|
-
jacs.load('./jacs.config.json');
|
|
190
|
+
await jacs.load('./jacs.config.json');
|
|
127
191
|
|
|
128
192
|
// Sign data
|
|
129
|
-
const signed = jacs.signMessage({
|
|
193
|
+
const signed = await jacs.signMessage({
|
|
130
194
|
action: 'transfer',
|
|
131
195
|
amount: 500,
|
|
132
196
|
to: 'agent-123'
|
|
133
197
|
});
|
|
134
198
|
|
|
135
199
|
// Later, verify received data
|
|
136
|
-
const result = jacs.verify(receivedJson);
|
|
200
|
+
const result = await jacs.verify(receivedJson);
|
|
137
201
|
if (result.valid) {
|
|
138
202
|
console.log(`Signed by: ${result.signerId}`);
|
|
139
203
|
console.log(`Data: ${JSON.stringify(result.data)}`);
|
|
@@ -146,7 +210,7 @@ if (result.valid) {
|
|
|
146
210
|
// Get current agent, modify, and update
|
|
147
211
|
const agentDoc = JSON.parse(jacs.exportAgent());
|
|
148
212
|
agentDoc.jacsAgentType = 'updated-service';
|
|
149
|
-
const updated = jacs.updateAgent(agentDoc);
|
|
213
|
+
const updated = await jacs.updateAgent(agentDoc);
|
|
150
214
|
console.log('Agent updated with new version');
|
|
151
215
|
```
|
|
152
216
|
|
|
@@ -154,12 +218,12 @@ console.log('Agent updated with new version');
|
|
|
154
218
|
|
|
155
219
|
```javascript
|
|
156
220
|
// Create a document
|
|
157
|
-
const signed = jacs.signMessage({ status: 'pending', amount: 100 });
|
|
221
|
+
const signed = await jacs.signMessage({ status: 'pending', amount: 100 });
|
|
158
222
|
|
|
159
223
|
// Later, update it
|
|
160
224
|
const doc = JSON.parse(signed.raw);
|
|
161
225
|
doc.content.status = 'approved';
|
|
162
|
-
const updated = jacs.updateDocument(signed.documentId, doc);
|
|
226
|
+
const updated = await jacs.updateDocument(signed.documentId, doc);
|
|
163
227
|
console.log('Document updated with new version');
|
|
164
228
|
```
|
|
165
229
|
|
|
@@ -167,28 +231,247 @@ console.log('Document updated with new version');
|
|
|
167
231
|
|
|
168
232
|
```javascript
|
|
169
233
|
// Reference only (stores hash)
|
|
170
|
-
const signed = jacs.signFile('contract.pdf', false);
|
|
234
|
+
const signed = await jacs.signFile('contract.pdf', false);
|
|
171
235
|
|
|
172
236
|
// Embed content (portable document)
|
|
173
|
-
const embedded = jacs.signFile('contract.pdf', true);
|
|
237
|
+
const embedded = await jacs.signFile('contract.pdf', true);
|
|
174
238
|
```
|
|
175
239
|
|
|
176
|
-
|
|
240
|
+
## Framework Adapters
|
|
177
241
|
|
|
178
|
-
|
|
242
|
+
### Vercel AI SDK (`@hai.ai/jacs/vercel-ai`)
|
|
179
243
|
|
|
180
|
-
|
|
244
|
+
Sign AI model outputs with cryptographic provenance using the AI SDK's middleware pattern:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
import { JacsClient } from '@hai.ai/jacs/client';
|
|
248
|
+
import { withProvenance } from '@hai.ai/jacs/vercel-ai';
|
|
249
|
+
import { openai } from '@ai-sdk/openai';
|
|
250
|
+
import { generateText } from 'ai';
|
|
251
|
+
|
|
252
|
+
const client = await JacsClient.quickstart();
|
|
253
|
+
const model = withProvenance(openai('gpt-4o'), { client });
|
|
254
|
+
|
|
255
|
+
const { text, providerMetadata } = await generateText({ model, prompt: 'Hello!' });
|
|
256
|
+
console.log(providerMetadata?.jacs?.text?.documentId); // signed proof
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Works with `generateText`, `streamText` (signs after stream completes), and tool calls. Compose with other middleware via `jacsProvenance()`.
|
|
260
|
+
|
|
261
|
+
**Peer deps**: `npm install ai @ai-sdk/provider`
|
|
262
|
+
|
|
263
|
+
### Express Middleware (`@hai.ai/jacs/express`)
|
|
264
|
+
|
|
265
|
+
Verify incoming signed requests, optionally auto-sign responses:
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
import express from 'express';
|
|
269
|
+
import { JacsClient } from '@hai.ai/jacs/client';
|
|
270
|
+
import { jacsMiddleware } from '@hai.ai/jacs/express';
|
|
271
|
+
|
|
272
|
+
const client = await JacsClient.quickstart();
|
|
273
|
+
const app = express();
|
|
274
|
+
app.use(express.text({ type: 'application/json' }));
|
|
275
|
+
app.use(jacsMiddleware({ client, verify: true }));
|
|
276
|
+
|
|
277
|
+
app.post('/api/data', (req, res) => {
|
|
278
|
+
console.log(req.jacsPayload); // verified payload
|
|
279
|
+
// Manual signing via req.jacsClient:
|
|
280
|
+
req.jacsClient.signMessage({ status: 'ok' }).then(signed => {
|
|
281
|
+
res.type('text/plain').send(signed.raw);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Options: `client`, `configPath`, `sign` (auto-sign, default false), `verify` (default true), `optional` (allow unsigned, default false). Supports Express v4 + v5.
|
|
287
|
+
|
|
288
|
+
**Peer dep**: `npm install express`
|
|
289
|
+
|
|
290
|
+
### Koa Middleware (`@hai.ai/jacs/koa`)
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import Koa from 'koa';
|
|
294
|
+
import { jacsKoaMiddleware } from '@hai.ai/jacs/koa';
|
|
295
|
+
|
|
296
|
+
const app = new Koa();
|
|
297
|
+
app.use(jacsKoaMiddleware({ client, verify: true, sign: true }));
|
|
298
|
+
app.use(async (ctx) => {
|
|
299
|
+
console.log(ctx.state.jacsPayload); // verified
|
|
300
|
+
ctx.body = { status: 'ok' }; // auto-signed when sign: true
|
|
301
|
+
});
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Peer dep**: `npm install koa`
|
|
305
|
+
|
|
306
|
+
### LangChain.js (`@hai.ai/jacs/langchain`)
|
|
307
|
+
|
|
308
|
+
Two integration patterns — full toolkit or auto-signing wrappers:
|
|
309
|
+
|
|
310
|
+
**Full toolkit** — give your LangChain agent access to all JACS operations (sign, verify, agreements, trust, audit):
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
import { JacsClient } from '@hai.ai/jacs/client';
|
|
314
|
+
import { createJacsTools } from '@hai.ai/jacs/langchain';
|
|
315
|
+
|
|
316
|
+
const client = await JacsClient.quickstart();
|
|
317
|
+
const jacsTools = createJacsTools({ client });
|
|
318
|
+
|
|
319
|
+
// Bind to your LLM — agent can now sign, verify, create agreements, etc.
|
|
320
|
+
const llm = model.bindTools([...myTools, ...jacsTools]);
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Returns 11 tools: `jacs_sign`, `jacs_verify`, `jacs_create_agreement`, `jacs_sign_agreement`, `jacs_check_agreement`, `jacs_verify_self`, `jacs_trust_agent`, `jacs_list_trusted`, `jacs_is_trusted`, `jacs_audit`, `jacs_agent_info`.
|
|
324
|
+
|
|
325
|
+
**Auto-signing wrappers** — transparently sign existing tool outputs:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
import { signedTool, jacsToolNode } from '@hai.ai/jacs/langchain';
|
|
329
|
+
|
|
330
|
+
// Wrap a single tool
|
|
331
|
+
const signed = signedTool(myTool, { client });
|
|
332
|
+
|
|
333
|
+
// Or wrap all tools in a ToolNode (LangGraph)
|
|
334
|
+
const node = jacsToolNode([tool1, tool2], { client });
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**Peer deps**: `npm install @langchain/core` (and optionally `@langchain/langgraph` for `jacsToolNode`)
|
|
338
|
+
|
|
339
|
+
### MCP (`@hai.ai/jacs/mcp`)
|
|
340
|
+
|
|
341
|
+
Two integration patterns — transport proxy or full tool registration:
|
|
342
|
+
|
|
343
|
+
**Transport proxy** — wrap any MCP transport with signing/verification:
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { JacsClient } from '@hai.ai/jacs/client';
|
|
181
347
|
import { createJACSTransportProxy } from '@hai.ai/jacs/mcp';
|
|
182
348
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
183
349
|
|
|
184
|
-
|
|
350
|
+
const client = await JacsClient.quickstart();
|
|
185
351
|
const baseTransport = new StdioServerTransport();
|
|
186
|
-
const
|
|
187
|
-
|
|
352
|
+
const secureTransport = createJACSTransportProxy(baseTransport, client, 'server');
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**MCP tool registration** — add all JACS tools to your MCP server (mirrors the Rust `jacs-mcp` server):
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
359
|
+
import { JacsClient } from '@hai.ai/jacs/client';
|
|
360
|
+
import { registerJacsTools } from '@hai.ai/jacs/mcp';
|
|
361
|
+
|
|
362
|
+
const server = new Server({ name: 'my-server', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
363
|
+
const client = await JacsClient.quickstart();
|
|
364
|
+
registerJacsTools(server, client);
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Registers 17 tools: signing, verification, agreements, trust store, audit, HAI integration, file signing, and more. Use `getJacsMcpToolDefinitions()` and `handleJacsMcpToolCall()` for custom integration.
|
|
368
|
+
|
|
369
|
+
**Peer dep**: `npm install @modelcontextprotocol/sdk`
|
|
370
|
+
|
|
371
|
+
### Legacy: `@hai.ai/jacs/http`
|
|
372
|
+
|
|
373
|
+
The old `JACSExpressMiddleware` and `JACSKoaMiddleware` are still available from `@hai.ai/jacs/http` for backward compatibility. New code should use `@hai.ai/jacs/express` and `@hai.ai/jacs/koa`.
|
|
374
|
+
|
|
375
|
+
## JacsClient (Instance-Based API)
|
|
376
|
+
|
|
377
|
+
`JacsClient` is the recommended API for new code. Each instance owns its own agent, so multiple clients can coexist in the same process without shared global state.
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
import { JacsClient } from '@hai.ai/jacs/client';
|
|
381
|
+
|
|
382
|
+
// Zero-config: loads or creates a persistent agent
|
|
383
|
+
const client = await JacsClient.quickstart({ algorithm: 'ring-Ed25519' });
|
|
384
|
+
|
|
385
|
+
const signed = await client.signMessage({ action: 'approve', amount: 100 });
|
|
386
|
+
const result = await client.verify(signed.raw);
|
|
387
|
+
console.log(`Valid: ${result.valid}, Signer: ${result.signerId}`);
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Ephemeral Clients
|
|
391
|
+
|
|
392
|
+
For testing or throwaway use, create an in-memory client with no files or env vars:
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
const client = await JacsClient.ephemeral('ring-Ed25519');
|
|
396
|
+
const signed = await client.signMessage({ hello: 'world' });
|
|
397
|
+
const result = await client.verify(signed.raw);
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Sync variants are also available:
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
const client = JacsClient.ephemeralSync('ring-Ed25519');
|
|
404
|
+
const signed = client.signMessageSync({ hello: 'world' });
|
|
405
|
+
const result = client.verifySync(signed.raw);
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Multi-Party Agreements
|
|
409
|
+
|
|
410
|
+
Create agreements that require signatures from multiple agents, with optional constraints:
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
const agreement = await client.createAgreement(
|
|
414
|
+
{ action: 'deploy', version: '2.0' },
|
|
415
|
+
[agentA.agentId, agentB.agentId],
|
|
416
|
+
{
|
|
417
|
+
question: 'Approve deployment?',
|
|
418
|
+
timeout: '2026-03-01T00:00:00Z', // ISO 8601 deadline
|
|
419
|
+
quorum: 2, // M-of-N signatures required
|
|
420
|
+
requiredAlgorithms: ['ring-Ed25519'], // restrict signing algorithms
|
|
421
|
+
minimumStrength: 'classical', // "classical" or "post-quantum"
|
|
422
|
+
},
|
|
188
423
|
);
|
|
424
|
+
|
|
425
|
+
// Other agents sign the agreement
|
|
426
|
+
const signed = await agentB.signAgreement(agreement.raw);
|
|
427
|
+
|
|
428
|
+
// Check agreement status
|
|
429
|
+
const status = await client.checkAgreement(signed.raw);
|
|
430
|
+
console.log(`Complete: ${status.complete}, Signatures: ${status.signedCount}/${status.totalRequired}`);
|
|
189
431
|
```
|
|
190
432
|
|
|
191
|
-
|
|
433
|
+
### JacsClient API
|
|
434
|
+
|
|
435
|
+
All instance methods have async (default) and sync variants:
|
|
436
|
+
|
|
437
|
+
| Method | Sync Variant | Description |
|
|
438
|
+
|--------|-------------|-------------|
|
|
439
|
+
| `JacsClient.quickstart(options?)` | `JacsClient.quickstartSync(options?)` | Load or create a persistent agent |
|
|
440
|
+
| `JacsClient.ephemeral(algorithm?)` | `JacsClient.ephemeralSync(algorithm?)` | Create an in-memory agent |
|
|
441
|
+
| `client.load(configPath?)` | `client.loadSync(configPath?)` | Load agent from config file |
|
|
442
|
+
| `client.create(options)` | `client.createSync(options)` | Create a new agent |
|
|
443
|
+
| `client.signMessage(data)` | `client.signMessageSync(data)` | Sign any JSON data |
|
|
444
|
+
| `client.verify(doc)` | `client.verifySync(doc)` | Verify a signed document |
|
|
445
|
+
| `client.verifySelf()` | `client.verifySelfSync()` | Verify agent's own integrity |
|
|
446
|
+
| `client.verifyById(id)` | `client.verifyByIdSync(id)` | Verify by storage ID |
|
|
447
|
+
| `client.signFile(path, embed?)` | `client.signFileSync(path, embed?)` | Sign a file |
|
|
448
|
+
| `client.createAgreement(...)` | `client.createAgreementSync(...)` | Create multi-party agreement |
|
|
449
|
+
| `client.signAgreement(...)` | `client.signAgreementSync(...)` | Sign an agreement |
|
|
450
|
+
| `client.checkAgreement(...)` | `client.checkAgreementSync(...)` | Check agreement status |
|
|
451
|
+
| `client.updateAgent(data)` | `client.updateAgentSync(data)` | Update agent document |
|
|
452
|
+
| `client.updateDocument(id, data)` | `client.updateDocumentSync(id, data)` | Update a document |
|
|
453
|
+
|
|
454
|
+
See [`examples/multi_agent_agreement.ts`](./examples/multi_agent_agreement.ts) for a complete multi-agent agreement demo.
|
|
455
|
+
|
|
456
|
+
## Testing
|
|
457
|
+
|
|
458
|
+
The `@hai.ai/jacs/testing` module provides zero-setup test helpers:
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
import { createTestClient, createTestClientSync } from '@hai.ai/jacs/testing';
|
|
462
|
+
|
|
463
|
+
// Async (preferred)
|
|
464
|
+
const client = await createTestClient('ring-Ed25519');
|
|
465
|
+
const signed = await client.signMessage({ hello: 'test' });
|
|
466
|
+
const result = await client.verify(signed.raw);
|
|
467
|
+
assert(result.valid);
|
|
468
|
+
|
|
469
|
+
// Sync
|
|
470
|
+
const client2 = createTestClientSync('ring-Ed25519');
|
|
471
|
+
const signed2 = client2.signMessageSync({ hello: 'test' });
|
|
472
|
+
const result2 = client2.verifySync(signed2.raw);
|
|
473
|
+
assert(result2.valid);
|
|
474
|
+
```
|
|
192
475
|
|
|
193
476
|
## HAI Integration
|
|
194
477
|
|
|
@@ -232,6 +515,7 @@ interface RemotePublicKeyInfo {
|
|
|
232
515
|
|
|
233
516
|
- [JACS Book](https://humanassisted.github.io/JACS/) - Full documentation (published book)
|
|
234
517
|
- [Quick Start](https://humanassisted.github.io/JACS/getting-started/quick-start.html)
|
|
518
|
+
- [Verification Guide](https://humanassisted.github.io/JACS/getting-started/verification.html) - CLI, standalone, DNS verification
|
|
235
519
|
- [Source](https://github.com/HumanAssisted/JACS) - GitHub repository
|
|
236
520
|
- [HAI Developer Portal](https://hai.ai/dev)
|
|
237
521
|
- [Examples](./examples/)
|
package/client.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JACS Instance-Based Client API
|
|
3
|
+
*
|
|
4
|
+
* v0.7.0: Async-first API. All methods that call native JACS operations
|
|
5
|
+
* return Promises by default. Use `*Sync` variants for synchronous execution.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { JacsClient } from '@hai.ai/jacs/client';
|
|
10
|
+
*
|
|
11
|
+
* const client = await JacsClient.quickstart({ algorithm: 'ring-Ed25519' });
|
|
12
|
+
* const signed = await client.signMessage({ action: 'approve' });
|
|
13
|
+
* const result = await client.verify(signed.raw);
|
|
14
|
+
* console.log(`Valid: ${result.valid}`);
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { hashString, createConfig } from './index';
|
|
18
|
+
import { generateVerifyLink, MAX_VERIFY_URL_LEN, MAX_VERIFY_DOCUMENT_BYTES } from './simple';
|
|
19
|
+
import type { AgentInfo, SignedDocument, VerificationResult, Attachment, AgreementStatus, AuditOptions, QuickstartOptions, QuickstartInfo, CreateAgentOptions, LoadOptions } from './simple';
|
|
20
|
+
export type { AgentInfo, SignedDocument, VerificationResult, Attachment, AgreementStatus, AuditOptions, QuickstartOptions, QuickstartInfo, CreateAgentOptions, LoadOptions, };
|
|
21
|
+
export { hashString, createConfig, generateVerifyLink, MAX_VERIFY_URL_LEN, MAX_VERIFY_DOCUMENT_BYTES };
|
|
22
|
+
export interface AgreementOptions {
|
|
23
|
+
question?: string;
|
|
24
|
+
context?: string;
|
|
25
|
+
fieldName?: string;
|
|
26
|
+
timeout?: string;
|
|
27
|
+
quorum?: number;
|
|
28
|
+
requiredAlgorithms?: string[];
|
|
29
|
+
minimumStrength?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface JacsClientOptions {
|
|
32
|
+
configPath?: string;
|
|
33
|
+
algorithm?: string;
|
|
34
|
+
strict?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare class JacsClient {
|
|
37
|
+
private agent;
|
|
38
|
+
private info;
|
|
39
|
+
private _strict;
|
|
40
|
+
constructor(options?: JacsClientOptions);
|
|
41
|
+
/**
|
|
42
|
+
* Zero-config factory: loads or creates a persistent agent.
|
|
43
|
+
*/
|
|
44
|
+
static quickstart(options?: QuickstartOptions): Promise<JacsClient>;
|
|
45
|
+
/**
|
|
46
|
+
* Zero-config factory (sync variant).
|
|
47
|
+
*/
|
|
48
|
+
static quickstartSync(options?: QuickstartOptions): JacsClient;
|
|
49
|
+
/**
|
|
50
|
+
* Create an ephemeral in-memory client for testing.
|
|
51
|
+
*/
|
|
52
|
+
static ephemeral(algorithm?: string): Promise<JacsClient>;
|
|
53
|
+
/**
|
|
54
|
+
* Create an ephemeral in-memory client (sync variant).
|
|
55
|
+
*/
|
|
56
|
+
static ephemeralSync(algorithm?: string): JacsClient;
|
|
57
|
+
load(configPath?: string, options?: LoadOptions): Promise<AgentInfo>;
|
|
58
|
+
loadSync(configPath?: string, options?: LoadOptions): AgentInfo;
|
|
59
|
+
create(options: CreateAgentOptions): Promise<AgentInfo>;
|
|
60
|
+
createSync(options: CreateAgentOptions): AgentInfo;
|
|
61
|
+
reset(): void;
|
|
62
|
+
dispose(): void;
|
|
63
|
+
[Symbol.dispose](): void;
|
|
64
|
+
get agentId(): string;
|
|
65
|
+
get name(): string;
|
|
66
|
+
get strict(): boolean;
|
|
67
|
+
private requireAgent;
|
|
68
|
+
signMessage(data: any): Promise<SignedDocument>;
|
|
69
|
+
signMessageSync(data: any): SignedDocument;
|
|
70
|
+
verify(signedDocument: string): Promise<VerificationResult>;
|
|
71
|
+
verifySync(signedDocument: string): VerificationResult;
|
|
72
|
+
verifySelf(): Promise<VerificationResult>;
|
|
73
|
+
verifySelfSync(): VerificationResult;
|
|
74
|
+
verifyById(documentId: string): Promise<VerificationResult>;
|
|
75
|
+
verifyByIdSync(documentId: string): VerificationResult;
|
|
76
|
+
signFile(filePath: string, embed?: boolean): Promise<SignedDocument>;
|
|
77
|
+
signFileSync(filePath: string, embed?: boolean): SignedDocument;
|
|
78
|
+
createAgreement(document: any, agentIds: string[], options?: AgreementOptions): Promise<SignedDocument>;
|
|
79
|
+
createAgreementSync(document: any, agentIds: string[], options?: AgreementOptions): SignedDocument;
|
|
80
|
+
signAgreement(document: any, fieldName?: string): Promise<SignedDocument>;
|
|
81
|
+
signAgreementSync(document: any, fieldName?: string): SignedDocument;
|
|
82
|
+
checkAgreement(document: any, fieldName?: string): Promise<AgreementStatus>;
|
|
83
|
+
checkAgreementSync(document: any, fieldName?: string): AgreementStatus;
|
|
84
|
+
updateAgent(newAgentData: any): Promise<string>;
|
|
85
|
+
updateAgentSync(newAgentData: any): string;
|
|
86
|
+
updateDocument(documentId: string, newDocumentData: any, attachments?: string[], embed?: boolean): Promise<SignedDocument>;
|
|
87
|
+
updateDocumentSync(documentId: string, newDocumentData: any, attachments?: string[], embed?: boolean): SignedDocument;
|
|
88
|
+
trustAgent(agentJson: string): string;
|
|
89
|
+
listTrustedAgents(): string[];
|
|
90
|
+
untrustAgent(agentId: string): void;
|
|
91
|
+
isTrusted(agentId: string): boolean;
|
|
92
|
+
getTrustedAgent(agentId: string): string;
|
|
93
|
+
audit(options?: AuditOptions): Promise<Record<string, unknown>>;
|
|
94
|
+
auditSync(options?: AuditOptions): Record<string, unknown>;
|
|
95
|
+
generateVerifyLink(document: string, baseUrl?: string): string;
|
|
96
|
+
}
|