@nomosprotocol/sdk 1.3.1 → 1.3.3

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