@nomosprotocol/sdk 1.3.0 → 1.3.1
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 +75 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
# @nomosprotocol/sdk
|
|
2
2
|
|
|
3
|
-
Official TypeScript SDK for [NOMOS](https://www.nomosprotocol.com) — ask
|
|
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.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install @nomosprotocol/sdk
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
## `
|
|
9
|
+
## `can()` — one function, three ways to ask
|
|
10
10
|
|
|
11
|
-
|
|
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.
|
|
12
|
+
|
|
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 |
|
|
18
|
+
|
|
19
|
+
### Public Exchange authority (keyless)
|
|
20
|
+
|
|
21
|
+
No API key, no setup — this is the front door.
|
|
12
22
|
|
|
13
23
|
```ts
|
|
14
24
|
import { nomos } from "@nomosprotocol/sdk";
|
|
@@ -35,9 +45,44 @@ const verdict = await nomos.can({
|
|
|
35
45
|
});
|
|
36
46
|
```
|
|
37
47
|
|
|
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).
|
|
49
|
+
|
|
50
|
+
### Your own artifact (authenticated)
|
|
51
|
+
|
|
52
|
+
Same function, same verdict shape — swap `authority` for `artifact_id` and use the authenticated client:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { Nomos } from "@nomosprotocol/sdk";
|
|
56
|
+
|
|
57
|
+
const client = new Nomos("nms_live_..."); // or "nms_test_..." for test mode
|
|
58
|
+
|
|
59
|
+
const verdict = await client.can({
|
|
60
|
+
artifact_id: "loan_approval_v1",
|
|
61
|
+
credit_score: 720,
|
|
62
|
+
loan_amount: 50_000,
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Chain-of-trust (keyless, prototype)
|
|
67
|
+
|
|
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:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const verdict = await nomos.can({
|
|
72
|
+
artifact: theArtifactYouWereHanded,
|
|
73
|
+
key_certs: theCertificatesTheyIncluded,
|
|
74
|
+
root_public_key_pem: yourPinnedRootKey,
|
|
75
|
+
credit_score: 720,
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
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).
|
|
80
|
+
|
|
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.
|
|
82
|
+
|
|
38
83
|
### The verdict
|
|
39
84
|
|
|
40
|
-
`
|
|
85
|
+
`can()` always returns the authority's own verdict, verbatim — nothing invented, nothing paraphrased — normalized into the same shape regardless of which rail answered:
|
|
41
86
|
|
|
42
87
|
| Field | Type | Meaning |
|
|
43
88
|
|---|---|---|
|
|
@@ -45,27 +90,44 @@ const verdict = await nomos.can({
|
|
|
45
90
|
| `verdict` | `"AUTHORIZED" \| "DENIED" \| "ESCALATED"` | The authority's own answer |
|
|
46
91
|
| `reason` | `string` | Human-readable explanation |
|
|
47
92
|
| `rule` | `string \| null` | The rule id that decided it, if any |
|
|
48
|
-
| `query_id` | `string` | Permanent
|
|
49
|
-
| `url` | `string` | Path to the transcript's permalink page |
|
|
50
|
-
| `authority` | `{ id, title, standing }` | Which authority answered,
|
|
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 |
|
|
51
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 |
|
|
52
103
|
|
|
53
104
|
`ESCALATED` means the authority needs more facts to decide — not a flat no. Check `missing_inputs` for what to supply.
|
|
54
105
|
|
|
55
|
-
|
|
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.
|
|
56
107
|
|
|
57
108
|
### Error handling
|
|
58
109
|
|
|
59
|
-
A failed call
|
|
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:
|
|
60
111
|
|
|
61
112
|
```ts
|
|
62
|
-
import {
|
|
113
|
+
import {
|
|
114
|
+
nomos,
|
|
115
|
+
NomosNotFoundError,
|
|
116
|
+
NomosNetworkError,
|
|
117
|
+
NomosPaymentRequiredError,
|
|
118
|
+
NomosIssuerNotTrustedError,
|
|
119
|
+
} from "@nomosprotocol/sdk";
|
|
63
120
|
|
|
64
121
|
try {
|
|
65
122
|
const verdict = await nomos.can({ authority: "eu-ai-act.nomos", ...facts });
|
|
66
123
|
} catch (err) {
|
|
67
124
|
if (err instanceof NomosNotFoundError) {
|
|
68
125
|
// authority slug/id doesn't exist — err.code === 'not_found'
|
|
126
|
+
} else if (err instanceof NomosPaymentRequiredError) {
|
|
127
|
+
// paid, unpurchased — err.priceUsd / err.purchaseUrl
|
|
128
|
+
} else if (err instanceof NomosIssuerNotTrustedError) {
|
|
129
|
+
// chain-of-trust rail only — err.reason is one of
|
|
130
|
+
// ISSUER_NOT_RECOGNIZED | KEY_REVOKED | SEAL_INVALID
|
|
69
131
|
} else if (err instanceof NomosNetworkError) {
|
|
70
132
|
// request timed out or the network failed
|
|
71
133
|
} else {
|
|
@@ -88,13 +150,15 @@ await nomos.can(
|
|
|
88
150
|
|
|
89
151
|
## `Nomos` — authenticated client
|
|
90
152
|
|
|
91
|
-
|
|
153
|
+
Beyond `client.can()`, the authenticated client covers the rest of the platform — the raw (richer) decision-verification shape, governance generation, and webhook verification:
|
|
92
154
|
|
|
93
155
|
```ts
|
|
94
156
|
import { Nomos } from "@nomosprotocol/sdk";
|
|
95
157
|
|
|
96
158
|
const client = new Nomos("nms_live_..."); // or "nms_test_..." for test mode
|
|
97
159
|
|
|
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.
|
|
98
162
|
const result = await client.decisions.verify({
|
|
99
163
|
artifact_id: "your_artifact_id",
|
|
100
164
|
decision_context: { /* facts */ },
|