@nomosprotocol/sdk 1.3.0 → 1.3.2
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 +320 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,14 +1,36 @@
|
|
|
1
1
|
# @nomosprotocol/sdk
|
|
2
2
|
|
|
3
|
-
Official TypeScript SDK for [NOMOS](https://www.nomosprotocol.com) —
|
|
3
|
+
Official TypeScript SDK for [NOMOS](https://www.nomosprotocol.com/) — a programmable authority layer for AI agents and software.
|
|
4
|
+
|
|
5
|
+
NOMOS lets software ask a simple question:
|
|
6
|
+
|
|
7
|
+
> **Can I do this?**
|
|
8
|
+
|
|
9
|
+
Give `can()` an authority, an action, and the relevant facts. NOMOS verifies the authority, evaluates its rules, and returns a deterministic verdict:
|
|
10
|
+
|
|
11
|
+
**AUTHORIZED · DENIED · ESCALATED**
|
|
4
12
|
|
|
5
13
|
```bash
|
|
6
14
|
npm install @nomosprotocol/sdk
|
|
7
15
|
```
|
|
8
16
|
|
|
9
|
-
## `
|
|
17
|
+
## `can()` — one function for authority
|
|
18
|
+
|
|
19
|
+
`can()` is the primary query interface to NOMOS.
|
|
20
|
+
|
|
21
|
+
It abstracts three different authority situations behind one call and one verdict vocabulary:
|
|
22
|
+
|
|
23
|
+
| You have... | Call | API key? |
|
|
24
|
+
| ---------------------------------------------------------- | ------------------------------------------------------------------- | -------: |
|
|
25
|
+
| A public authority published on the NOMOS Exchange | `nomos.can({ authority, ...facts })` | No |
|
|
26
|
+
| Your organization's sealed artifact | `client.can({ artifact_id, ...facts })` | Yes |
|
|
27
|
+
| A sealed artifact presented directly by an agent or system | `nomos.can({ artifact, key_certs, root_public_key_pem, ...facts })` | No |
|
|
28
|
+
|
|
29
|
+
The caller does not need to know which evaluation rail is being used. `can()` routes internally based on what you provide.
|
|
10
30
|
|
|
11
|
-
|
|
31
|
+
### 1. Public authority
|
|
32
|
+
|
|
33
|
+
Ask a published authority directly:
|
|
12
34
|
|
|
13
35
|
```ts
|
|
14
36
|
import { nomos } from "@nomosprotocol/sdk";
|
|
@@ -18,101 +40,352 @@ const verdict = await nomos.can({
|
|
|
18
40
|
ai_categorises_biometric_data_for_sensitive_attributes: true,
|
|
19
41
|
});
|
|
20
42
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
43
|
+
console.log(verdict.verdict);
|
|
44
|
+
// "DENIED"
|
|
45
|
+
|
|
46
|
+
console.log(verdict.reason);
|
|
47
|
+
// explanation from the authority
|
|
48
|
+
|
|
49
|
+
console.log(verdict.query_id);
|
|
50
|
+
// permanent transcript ID
|
|
26
51
|
```
|
|
27
52
|
|
|
28
|
-
|
|
53
|
+
No API key or account is required for public Exchange authorities.
|
|
54
|
+
|
|
55
|
+
Facts can also be passed explicitly:
|
|
29
56
|
|
|
30
57
|
```ts
|
|
31
58
|
const verdict = await nomos.can({
|
|
32
59
|
authority: "eu-ai-act.nomos",
|
|
33
|
-
inputs: {
|
|
34
|
-
|
|
60
|
+
inputs: {
|
|
61
|
+
ai_categorises_biometric_data_for_sensitive_attributes: true,
|
|
62
|
+
},
|
|
63
|
+
action: "screen a new feature before launch",
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
If a paid authority has not been purchased, NOMOS throws `NomosPaymentRequiredError` with the price and purchase URL attached.
|
|
68
|
+
|
|
69
|
+
NOMOS never auto-charges on your behalf.
|
|
70
|
+
|
|
71
|
+
### 2. Your organization's authority
|
|
72
|
+
|
|
73
|
+
Use an authenticated client for artifacts belonging to your organization:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { Nomos } from "@nomosprotocol/sdk";
|
|
77
|
+
|
|
78
|
+
const client = new Nomos("nms_live_...");
|
|
79
|
+
|
|
80
|
+
const verdict = await client.can({
|
|
81
|
+
artifact_id: "loan_approval_v1",
|
|
82
|
+
credit_score: 720,
|
|
83
|
+
loan_amount: 50_000,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (verdict.allowed) {
|
|
87
|
+
// proceed
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The same verdict vocabulary is returned.
|
|
92
|
+
|
|
93
|
+
The difference is that the artifact is private and accessed through your authenticated NOMOS account.
|
|
94
|
+
|
|
95
|
+
### 3. A portable authority presented by an agent
|
|
96
|
+
|
|
97
|
+
This is the keyless, portable authority rail.
|
|
98
|
+
|
|
99
|
+
An agent can carry a sealed `.nomos` artifact with it and present that authority to another system — even if NOMOS has never seen or registered the artifact.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const verdict = await nomos.can({
|
|
103
|
+
artifact: presentedArtifact,
|
|
104
|
+
key_certs: [rootToIntermediate, intermediateToLeaf],
|
|
105
|
+
root_public_key_pem: yourPinnedRootKey,
|
|
106
|
+
credit_score: 720,
|
|
35
107
|
});
|
|
36
108
|
```
|
|
37
109
|
|
|
38
|
-
|
|
110
|
+
NOMOS performs the entire verification locally:
|
|
111
|
+
|
|
112
|
+
1. Walk the presented certificate chain from the root key you've pinned.
|
|
113
|
+
2. Verify each link's signature and check it hasn't expired.
|
|
114
|
+
3. Check that no key in the chain has been revoked.
|
|
115
|
+
4. Verify the artifact's own cryptographic seal against the resolved key.
|
|
116
|
+
5. Evaluate the artifact's rules against the supplied facts.
|
|
117
|
+
6. Return `AUTHORIZED`, `DENIED`, or `ESCALATED`.
|
|
118
|
+
|
|
119
|
+
(NOMOS-SPEC-006 artifact-version revocation — an issuer withdrawing one specific sealed version — is checked on the Exchange rail today; wiring the same check into this rail is a known gap, not yet done.)
|
|
120
|
+
|
|
121
|
+
No prior artifact lookup is required.
|
|
122
|
+
|
|
123
|
+
This means authority can **travel with the agent**.
|
|
124
|
+
|
|
125
|
+
The relying party does not have to trust the AI vendor, the model, or a pre-existing NOMOS registry entry. It can independently verify the authority presented to it.
|
|
126
|
+
|
|
127
|
+
The root key is deliberately supplied by the relying party. NOMOS does not define a universal default root of trust.
|
|
128
|
+
|
|
129
|
+
This rail is currently a working prototype and is not yet a numbered NOMOS specification. A second independent implementation is still required before treating the wire format as interoperable infrastructure.
|
|
130
|
+
|
|
131
|
+
## `.nomos` — the portable authority artifact
|
|
39
132
|
|
|
40
|
-
|
|
133
|
+
A `.nomos` file is a sealed, machine-executable representation of institutional authority.
|
|
41
134
|
|
|
42
|
-
|
|
43
|
-
|---|---|---|
|
|
44
|
-
| `allowed` | `boolean` | `true` only when `verdict === "AUTHORIZED"` |
|
|
45
|
-
| `verdict` | `"AUTHORIZED" \| "DENIED" \| "ESCALATED"` | The authority's own answer |
|
|
46
|
-
| `reason` | `string` | Human-readable explanation |
|
|
47
|
-
| `rule` | `string \| null` | The rule id that decided it, if any |
|
|
48
|
-
| `query_id` | `string` | Permanent, addressable transcript id |
|
|
49
|
-
| `url` | `string` | Path to the transcript's permalink page |
|
|
50
|
-
| `authority` | `{ id, title, standing }` | Which authority answered, and its standing |
|
|
51
|
-
| `missing_inputs` | `string[]` | Fields the authority needed but didn't get — present on `ESCALATED` |
|
|
135
|
+
It can contain the rules, constraints, permissions, obligations, provenance, issuer information, version, and other governance metadata required to evaluate an action.
|
|
52
136
|
|
|
53
|
-
|
|
137
|
+
The artifact is cryptographically sealed.
|
|
54
138
|
|
|
55
|
-
|
|
139
|
+
Changing the authority changes the seal.
|
|
56
140
|
|
|
57
|
-
|
|
141
|
+
That makes the authority:
|
|
58
142
|
|
|
59
|
-
|
|
143
|
+
* **machine-executable** — software can evaluate it;
|
|
144
|
+
* **portable** — it can move between systems;
|
|
145
|
+
* **verifiable** — another party can check its authenticity;
|
|
146
|
+
* **versioned** — the exact authority being evaluated can be identified;
|
|
147
|
+
* **auditable** — decisions can be tied back to the authority that produced them.
|
|
148
|
+
|
|
149
|
+
The important distinction is:
|
|
150
|
+
|
|
151
|
+
> The AI does not have to carry the policy in its prompt and be trusted to obey it.
|
|
152
|
+
|
|
153
|
+
The authority can exist outside the model as a verifiable artifact.
|
|
154
|
+
|
|
155
|
+
The model reasons.
|
|
156
|
+
|
|
157
|
+
**NOMOS determines what the authority permits.**
|
|
158
|
+
|
|
159
|
+
## The verdict
|
|
160
|
+
|
|
161
|
+
`can()` normalizes every rail into the same core result:
|
|
162
|
+
|
|
163
|
+
| Field | Type | Meaning |
|
|
164
|
+
| ------------------- | ------------------------------------ | -------------------------------------------------------- |
|
|
165
|
+
| `allowed` | `boolean` | `true` only when the verdict is `AUTHORIZED` |
|
|
166
|
+
| `verdict` | `AUTHORIZED \| DENIED \| ESCALATED` | The authority's decision |
|
|
167
|
+
| `reason` | `string` | Human-readable explanation |
|
|
168
|
+
| `rule` | `string \| null` | Rule that determined the result, when applicable |
|
|
169
|
+
| `authority` | `{ id, title, standing, attestation? }` | The authority that answered, its standing, and — when a regulator/auditor/issuer has co-signed this exact version — who, in what role, and their statement |
|
|
170
|
+
| `version` | `string` | Authority version, when declared |
|
|
171
|
+
| `missing_inputs` | `string[]` | Facts required for an `ESCALATED` decision |
|
|
172
|
+
| `obligations` | array | Additional duties triggered alongside the verdict |
|
|
173
|
+
| `revoked` | `boolean` | Whether the evaluated authority version has been revoked |
|
|
174
|
+
| `revocation_reason` | `string` | Reason for revocation, when applicable |
|
|
175
|
+
| `max_age` | `number` | Recommended period before re-checking the verdict |
|
|
176
|
+
| `audit` | `{ hash, timestamp }` | Present only on your own artifact rail — your own provenance record, distinct from the Exchange rail's public transcript |
|
|
177
|
+
|
|
178
|
+
### `ESCALATED` is not `DENIED`
|
|
179
|
+
|
|
180
|
+
An escalation means the authority does not yet have enough information to decide.
|
|
60
181
|
|
|
61
182
|
```ts
|
|
62
|
-
|
|
183
|
+
if (verdict.verdict === "ESCALATED") {
|
|
184
|
+
console.log(verdict.missing_inputs);
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
This distinction matters in institutional workflows.
|
|
189
|
+
|
|
190
|
+
A missing fact is different from a prohibition.
|
|
191
|
+
|
|
192
|
+
An untrusted issuer is different from a rule saying "no."
|
|
193
|
+
|
|
194
|
+
NOMOS keeps those failure classes separate.
|
|
195
|
+
|
|
196
|
+
## Authority trust is separate from authorization
|
|
197
|
+
|
|
198
|
+
The portable-authority rail deliberately distinguishes two questions:
|
|
199
|
+
|
|
200
|
+
**"Do I trust this authority?"**
|
|
201
|
+
|
|
202
|
+
and
|
|
203
|
+
|
|
204
|
+
**"Does this authority permit this action?"**
|
|
205
|
+
|
|
206
|
+
If the presented chain cannot be established, NOMOS throws `NomosIssuerNotTrustedError`.
|
|
207
|
+
|
|
208
|
+
It does **not** return `DENIED`.
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
try {
|
|
212
|
+
const verdict = await nomos.can({
|
|
213
|
+
artifact,
|
|
214
|
+
key_certs,
|
|
215
|
+
root_public_key_pem,
|
|
216
|
+
credit_score: 720,
|
|
217
|
+
});
|
|
218
|
+
} catch (err) {
|
|
219
|
+
if (err instanceof NomosIssuerNotTrustedError) {
|
|
220
|
+
// The authority itself was not established as trustworthy.
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
A `DENIED` verdict means:
|
|
226
|
+
|
|
227
|
+
> **The authority was established, and its rules said no.**
|
|
228
|
+
|
|
229
|
+
An issuer-trust error means:
|
|
230
|
+
|
|
231
|
+
> **The authority itself was not established.**
|
|
232
|
+
|
|
233
|
+
Those are fundamentally different claims.
|
|
234
|
+
|
|
235
|
+
## Errors
|
|
236
|
+
|
|
237
|
+
NOMOS uses typed errors rather than generic failures:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import {
|
|
241
|
+
nomos,
|
|
242
|
+
NomosNotFoundError,
|
|
243
|
+
NomosNetworkError,
|
|
244
|
+
NomosPaymentRequiredError,
|
|
245
|
+
NomosIssuerNotTrustedError,
|
|
246
|
+
} from "@nomosprotocol/sdk";
|
|
63
247
|
|
|
64
248
|
try {
|
|
65
|
-
const verdict = await nomos.can({
|
|
249
|
+
const verdict = await nomos.can({
|
|
250
|
+
authority: "eu-ai-act.nomos",
|
|
251
|
+
...facts,
|
|
252
|
+
});
|
|
66
253
|
} catch (err) {
|
|
67
254
|
if (err instanceof NomosNotFoundError) {
|
|
68
|
-
//
|
|
255
|
+
// Authority does not exist.
|
|
256
|
+
} else if (err instanceof NomosPaymentRequiredError) {
|
|
257
|
+
// Paid authority that has not been purchased.
|
|
258
|
+
console.log(err.priceUsd);
|
|
259
|
+
console.log(err.purchaseUrl);
|
|
260
|
+
} else if (err instanceof NomosIssuerNotTrustedError) {
|
|
261
|
+
// Portable authority could not be established.
|
|
262
|
+
console.log(err.reason);
|
|
263
|
+
// ISSUER_NOT_RECOGNIZED
|
|
264
|
+
// KEY_REVOKED
|
|
265
|
+
// SEAL_INVALID
|
|
69
266
|
} else if (err instanceof NomosNetworkError) {
|
|
70
|
-
//
|
|
267
|
+
// Network failure or timeout.
|
|
71
268
|
} else {
|
|
72
269
|
throw err;
|
|
73
270
|
}
|
|
74
271
|
}
|
|
75
272
|
```
|
|
76
273
|
|
|
77
|
-
|
|
274
|
+
## Permanent decision transcripts
|
|
275
|
+
|
|
276
|
+
For public Exchange authorities, every `can()` request produces a permanent transcript.
|
|
277
|
+
|
|
278
|
+
The transcript records what was asked, which authority version answered, and what it decided.
|
|
279
|
+
|
|
280
|
+
The returned `query_id` can be used to retrieve the transcript:
|
|
78
281
|
|
|
79
282
|
```ts
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
{
|
|
83
|
-
baseUrl: "https://www.nomosprotocol.com", // override for local/staging use
|
|
84
|
-
signal: abortController.signal, // cancel the request
|
|
85
|
-
},
|
|
86
|
-
);
|
|
283
|
+
console.log(verdict.query_id);
|
|
284
|
+
console.log(verdict.url);
|
|
87
285
|
```
|
|
88
286
|
|
|
89
|
-
|
|
287
|
+
The portable and private artifact rails are stateless by design and do not invent a server-side transcript ID.
|
|
288
|
+
|
|
289
|
+
## What `can()` does — and does not do
|
|
90
290
|
|
|
91
|
-
|
|
291
|
+
`can()` answers one question:
|
|
292
|
+
|
|
293
|
+
> **Is this action authorized under this authority, given these facts, right now?**
|
|
294
|
+
|
|
295
|
+
It deliberately does not become the interface for everything NOMOS does. Other capabilities stay on the authenticated client, because they answer different questions:
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
await client.can({ artifact_id, ...facts }) // Is this action authorized?
|
|
299
|
+
await verifyChain({ artifact, key_certs, root_public_key_pem }) // Is this artifact's chain of custody genuine?
|
|
300
|
+
await client.artifacts.list({ band, sort }) // What artifacts are available?
|
|
301
|
+
await client.artifacts.retrieve(id) // What is inside this artifact?
|
|
302
|
+
await client.governance.generate({ policies, decision_logs }) // Create a new sealed artifact.
|
|
303
|
+
await client.governance.detectContradictions({ rules }) // Find conflicts in a rule set.
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The simplicity comes from giving each operation one meaning.
|
|
307
|
+
|
|
308
|
+
## Authenticated client
|
|
309
|
+
|
|
310
|
+
The authenticated `Nomos` client exposes the broader platform:
|
|
92
311
|
|
|
93
312
|
```ts
|
|
94
313
|
import { Nomos } from "@nomosprotocol/sdk";
|
|
95
314
|
|
|
96
|
-
const client = new Nomos("nms_live_...");
|
|
315
|
+
const client = new Nomos("nms_live_...");
|
|
97
316
|
|
|
98
|
-
|
|
317
|
+
// Authorization
|
|
318
|
+
await client.can({
|
|
99
319
|
artifact_id: "your_artifact_id",
|
|
100
|
-
|
|
320
|
+
...facts,
|
|
101
321
|
});
|
|
102
322
|
|
|
323
|
+
// Lower-level decision verification
|
|
324
|
+
await client.decisions.verify({
|
|
325
|
+
artifact_id: "your_artifact_id",
|
|
326
|
+
decision_context: { ...facts },
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Discover artifacts
|
|
103
330
|
for await (const artifact of client.artifacts.listAutoPaging()) {
|
|
104
331
|
console.log(artifact.id);
|
|
105
332
|
}
|
|
106
333
|
|
|
107
|
-
|
|
334
|
+
// Verify webhook events
|
|
335
|
+
const event = await client.webhooks.constructEvent(
|
|
336
|
+
payload,
|
|
337
|
+
signatureHeader,
|
|
338
|
+
webhookSecret,
|
|
339
|
+
);
|
|
108
340
|
```
|
|
109
341
|
|
|
110
|
-
|
|
342
|
+
`client.decisions.verify()` remains available when the caller needs the lower-level evaluation response rather than the normalized `can()` result.
|
|
343
|
+
|
|
344
|
+
## Why this exists
|
|
345
|
+
|
|
346
|
+
AI agents can reason.
|
|
347
|
+
|
|
348
|
+
They can plan.
|
|
349
|
+
|
|
350
|
+
They can call tools.
|
|
351
|
+
|
|
352
|
+
But reasoning about authority is not the same thing as possessing authority.
|
|
353
|
+
|
|
354
|
+
A system prompt can tell an agent:
|
|
355
|
+
|
|
356
|
+
> "You may approve loans below this threshold."
|
|
357
|
+
|
|
358
|
+
That does not independently prove that the instruction is authentic, current, untampered with, or actually authoritative.
|
|
359
|
+
|
|
360
|
+
NOMOS moves that authority outside the model.
|
|
361
|
+
|
|
362
|
+
The authority becomes a machine-readable artifact.
|
|
363
|
+
|
|
364
|
+
The artifact can be verified.
|
|
365
|
+
|
|
366
|
+
The rules can be evaluated deterministically.
|
|
367
|
+
|
|
368
|
+
And the result can be checked independently of the model that requested the action.
|
|
369
|
+
|
|
370
|
+
The long-term goal is simple:
|
|
371
|
+
|
|
372
|
+
> **AI agents should not have to be trusted to understand their authority. Their authority should be something other systems can verify.**
|
|
111
373
|
|
|
112
374
|
## Requirements
|
|
113
375
|
|
|
114
|
-
Node.js 18
|
|
376
|
+
* Node.js 18+
|
|
377
|
+
* TypeScript supported
|
|
378
|
+
* ESM and CommonJS builds
|
|
379
|
+
* Full TypeScript declarations
|
|
115
380
|
|
|
116
381
|
## License
|
|
117
382
|
|
|
118
383
|
MIT
|
|
384
|
+
|
|
385
|
+
## Links
|
|
386
|
+
|
|
387
|
+
* [NOMOS Protocol](https://www.nomosprotocol.com/)
|
|
388
|
+
* [Documentation](https://www.nomosprotocol.com/docs)
|
|
389
|
+
* [NOMOS Exchange](https://www.nomosprotocol.com/exchange)
|
|
390
|
+
* [NOMOS Specification](https://www.nomosprotocol.com/spec)
|
|
391
|
+
* [GitHub](https://github.com/nomos-spec/spec)
|