@nomosprotocol/sdk 2.5.0 → 2.6.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 +59 -1
- package/dist/index.cjs +54 -3
- package/dist/index.d.cts +75 -1
- package/dist/index.d.ts +75 -1
- package/dist/index.js +54 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -122,9 +122,55 @@ It abstracts three different authority situations behind one call and one verdic
|
|
|
122
122
|
| A public authority published on the NOMOS Exchange | `nomos.can({ authority, ...facts })` | No |
|
|
123
123
|
| Your organization's sealed artifact | `client.can({ artifact_id, ...facts })` | Yes |
|
|
124
124
|
| A sealed artifact presented directly by an agent or system | `nomos.can({ artifact, key_certs, root_public_key_pem, ...facts })` | No |
|
|
125
|
+
| An act, and you don't know which laws govern it | `nomos.can({ context, ...facts })` | No |
|
|
125
126
|
|
|
126
127
|
The caller does not need to know which evaluation rail is being used. `can()` routes internally based on what you provide.
|
|
127
128
|
|
|
129
|
+
### 0. Ask by context: let NOMOS find the laws (2.6.0)
|
|
130
|
+
|
|
131
|
+
Naming a law lets the caller choose which law is checked, and leave out the strict one. Describe
|
|
132
|
+
the act instead, and NOMOS works out which laws govern it, asks every one, and combines the answers:
|
|
133
|
+
any deny denies, and a yes needs every law to say yes.
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { can } from '@nomosprotocol/sdk';
|
|
137
|
+
|
|
138
|
+
const v = await can({
|
|
139
|
+
context: {
|
|
140
|
+
jurisdiction: 'AE', // where the act takes effect
|
|
141
|
+
established_in: 'DIFC', // where your organisation is incorporated or established
|
|
142
|
+
subjects_in: 'AE', // where the people it concerns are
|
|
143
|
+
output_used_in: 'AE', // where any AI output is used
|
|
144
|
+
},
|
|
145
|
+
action: 'share_customer_data_with_analytics_vendor',
|
|
146
|
+
processing_fair_and_transparent: false,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
v.mode; // 'resolution'
|
|
150
|
+
v.verdict; // 'DENIED': a DIFC firm acting in mainland Dubai answers to DIFC law
|
|
151
|
+
v.answered_under; // ['difc-data-protection-law-difc-law-no-5-of-2020']
|
|
152
|
+
v.coverage; // what the answer covers, in words
|
|
153
|
+
v.members; // each law's own answer, each a signed record (query_id)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
What can come back besides a verdict:
|
|
157
|
+
|
|
158
|
+
- `INSUFFICIENT_DATA` with `open_facts`: a fact that decides which law applies was not stated
|
|
159
|
+
(for example `established_in`). It is asked for, never assumed.
|
|
160
|
+
- `INSUFFICIENT_DATA` for a place the registrar does not cover (Saudi Arabia, the US, an ADGM or
|
|
161
|
+
QFC firm today). "No law found" is never read as "no law applies".
|
|
162
|
+
- `CONFLICT`: two registrars each claim exclusive precedence. A person decides.
|
|
163
|
+
|
|
164
|
+
Which laws apply where comes from the NOMOS registrar: a signed, append-only list in the public
|
|
165
|
+
log at `/.well-known/nomos-exchange-log`, verifiable offline. Each claim states the provision it
|
|
166
|
+
rests on. It currently covers the EU AI Act, Qatar Law No. 13 of 2016, UAE Federal Decree-Law
|
|
167
|
+
No. 45 of 2021 and DIFC Law No. 5 of 2020, and lists only its own laws, so other law may also
|
|
168
|
+
apply. Only want the list of laws? `POST /api/v1/resolve { context }` returns it without asking
|
|
169
|
+
them, and records nothing.
|
|
170
|
+
|
|
171
|
+
`accept()` still verifies the one authority an Act carries. Resolving inside `accept()`, from
|
|
172
|
+
registrars the receiving system pins, is the next step of NOMOS-SPEC-014.
|
|
173
|
+
|
|
128
174
|
### 1. Public authority
|
|
129
175
|
|
|
130
176
|
Ask a published authority directly:
|
|
@@ -187,7 +233,11 @@ import { Nomos } from "@nomosprotocol/sdk";
|
|
|
187
233
|
|
|
188
234
|
const client = new Nomos(process.env.NOMOS_API_KEY!);
|
|
189
235
|
const r = await client.can(
|
|
190
|
-
{ authority: "data-protection-law-difc-law-no-5-of-2020",
|
|
236
|
+
{ authority: "difc-data-protection-law-difc-law-no-5-of-2020",
|
|
237
|
+
...allTwelveNo, // the law's intake questions, listed on its page
|
|
238
|
+
q_individual_request: true, // "yes": a request from a person
|
|
239
|
+
access_request_received: true, days_since_access_request: 45,
|
|
240
|
+
identity_doubt_unresolved: false, response_period_extended_with_notice: false },
|
|
191
241
|
{ awaitApproval: { timeoutMs: 10 * 60_000, onApproval: (a) => console.log("waiting on", a.url) } },
|
|
192
242
|
);
|
|
193
243
|
|
|
@@ -196,6 +246,14 @@ else console.log(r.verdict, r.approval?.status); // DENIED, or rejected / timed_
|
|
|
196
246
|
```
|
|
197
247
|
|
|
198
248
|
The approval is bound to the authority, the action and the facts: different facts need a new one.
|
|
249
|
+
|
|
250
|
+
A large law carries a few yes/no **intake questions** (listed on its page). Each "no" rules out the
|
|
251
|
+
articles it covers, so an ordinary action gets a firm answer in one call: on the DIFC Data Protection
|
|
252
|
+
Law, all twelve "no" is `AUTHORIZED`.
|
|
253
|
+
|
|
254
|
+
When a listing is replaced by a newer version and retired, calls to the old address throw a
|
|
255
|
+
`NomosAPIError` with status 410: the message names the new version's address. Move to it; the old
|
|
256
|
+
address is never answered from the old rules.
|
|
199
257
|
When the law gets a new version, you get an email with what changed. Turn either email off on the
|
|
200
258
|
same page.
|
|
201
259
|
|
package/dist/index.cjs
CHANGED
|
@@ -191,7 +191,7 @@ function isRetryable(status) {
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
// src/http-client.ts
|
|
194
|
-
var SDK_VERSION = "2.
|
|
194
|
+
var SDK_VERSION = "2.6.0";
|
|
195
195
|
var HttpClient = class {
|
|
196
196
|
constructor(opts) {
|
|
197
197
|
this.opts = opts;
|
|
@@ -858,6 +858,50 @@ async function canViaChain(artifact, keyCerts, rootPublicKeyPem, facts, action,
|
|
|
858
858
|
if (data?.decision === "MALFORMED") throw new NomosValidationError(message, data, requestId);
|
|
859
859
|
mapHttpError(res.status, data, requestId);
|
|
860
860
|
}
|
|
861
|
+
function normalizeResolutionResult(data) {
|
|
862
|
+
return {
|
|
863
|
+
mode: "resolution",
|
|
864
|
+
allowed: data.verdict === "AUTHORIZED",
|
|
865
|
+
verdict: data.verdict,
|
|
866
|
+
reason: data.reason ?? "",
|
|
867
|
+
resolution: data.resolution ?? { status: "INCOMPLETE", proof: null },
|
|
868
|
+
members: Array.isArray(data.members) ? data.members : [],
|
|
869
|
+
deciding_authorities: Array.isArray(data.deciding_authorities) ? data.deciding_authorities : [],
|
|
870
|
+
escalations: Array.isArray(data.escalations) ? data.escalations : [],
|
|
871
|
+
open_facts: Array.isArray(data.open_facts) ? data.open_facts : [],
|
|
872
|
+
...Array.isArray(data.answered_under) ? { answered_under: data.answered_under } : {},
|
|
873
|
+
...typeof data.coverage === "string" ? { coverage: data.coverage } : {}
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function resolutionBody(params) {
|
|
877
|
+
const { authority, artifact_id, artifact, key_certs, root_public_key_pem, action, inputs, context, ...flat } = params;
|
|
878
|
+
void authority;
|
|
879
|
+
void artifact_id;
|
|
880
|
+
void artifact;
|
|
881
|
+
void key_certs;
|
|
882
|
+
void root_public_key_pem;
|
|
883
|
+
const facts = inputs && typeof inputs === "object" ? inputs : flat;
|
|
884
|
+
return { context, ...action ? { action } : {}, ...facts };
|
|
885
|
+
}
|
|
886
|
+
async function canViaResolution(params, opts) {
|
|
887
|
+
const base = (opts?.baseUrl ?? DEFAULT_EXCHANGE_BASE).replace(/\/$/, "");
|
|
888
|
+
let res;
|
|
889
|
+
try {
|
|
890
|
+
res = await fetch(`${base}/api/v1/can`, {
|
|
891
|
+
method: "POST",
|
|
892
|
+
headers: { "Content-Type": "application/json", "User-Agent": `nomos-node/${SDK_VERSION}`, ...callerHeaders(opts?.caller) },
|
|
893
|
+
body: JSON.stringify(resolutionBody(params)),
|
|
894
|
+
signal: opts?.signal
|
|
895
|
+
});
|
|
896
|
+
} catch (err) {
|
|
897
|
+
if (err?.name === "AbortError") throw new NomosNetworkError("Request aborted");
|
|
898
|
+
throw new NomosNetworkError(err?.message ?? "Network error");
|
|
899
|
+
}
|
|
900
|
+
const requestId = res.headers.get("x-request-id") ?? "";
|
|
901
|
+
const data = await res.json().catch(() => null);
|
|
902
|
+
if (!res.ok) mapHttpError(res.status, data, requestId);
|
|
903
|
+
return normalizeResolutionResult(data);
|
|
904
|
+
}
|
|
861
905
|
async function can(params, opts) {
|
|
862
906
|
if (params.artifact !== void 0) {
|
|
863
907
|
if (!params.key_certs || !params.root_public_key_pem) {
|
|
@@ -869,8 +913,11 @@ async function can(params, opts) {
|
|
|
869
913
|
if (params.artifact_id) {
|
|
870
914
|
throw new Error("can(): `artifact_id` requires an API key \u2014 use `client.can(...)` on an authenticated Nomos instance, not the free `can()` export.");
|
|
871
915
|
}
|
|
916
|
+
if (!params.authority && params.context && typeof params.context === "object" && !Array.isArray(params.context)) {
|
|
917
|
+
return canViaResolution(params, opts);
|
|
918
|
+
}
|
|
872
919
|
if (!params.authority || typeof params.authority !== "string") {
|
|
873
|
-
throw new Error("can(): `authority` (an Exchange artifact id or slug), `artifact_id` (via client.can()),
|
|
920
|
+
throw new Error("can(): `authority` (an Exchange artifact id or slug), `artifact_id` (via client.can()), `artifact` (chain-of-trust), or `context` (resolution, NOMOS-SPEC-014) is required.");
|
|
874
921
|
}
|
|
875
922
|
const { facts, action } = splitFacts(params);
|
|
876
923
|
return canViaExchange(params.authority, facts, action, opts);
|
|
@@ -885,7 +932,11 @@ async function canAuthenticated(http, params, opts) {
|
|
|
885
932
|
}
|
|
886
933
|
if (params.artifact_id) return canViaArtifact(http, params.artifact_id, facts, action);
|
|
887
934
|
if (params.authority) return canViaExchangeAuthenticated(http, params.authority, facts, action, opts);
|
|
888
|
-
|
|
935
|
+
if (params.context && typeof params.context === "object" && !Array.isArray(params.context)) {
|
|
936
|
+
const data = (await http.post("/api/v1/can", resolutionBody(params), { headers: callerHeaders(opts?.caller) })).data;
|
|
937
|
+
return normalizeResolutionResult(data);
|
|
938
|
+
}
|
|
939
|
+
throw new Error("can(): `authority` (a public Exchange artifact), `artifact_id` (your own artifact), `artifact` (chain-of-trust), or `context` (resolution, NOMOS-SPEC-014) is required.");
|
|
889
940
|
}
|
|
890
941
|
var nomos = { can };
|
|
891
942
|
|
package/dist/index.d.cts
CHANGED
|
@@ -80,9 +80,77 @@ interface CanParams {
|
|
|
80
80
|
/** The facts to evaluate. Either pass this explicitly, or spread the facts flat alongside
|
|
81
81
|
* `authority`/`action` — both shapes are accepted (explicit `inputs` wins if both given). */
|
|
82
82
|
inputs?: Record<string, unknown>;
|
|
83
|
+
/** Resolution rail (NOMOS-SPEC-014 Draft): the properties that attach law to an act —
|
|
84
|
+
* e.g. { jurisdiction: 'EU', sector: 'ai' }. With `context` and NO `authority`/`artifact_id`/
|
|
85
|
+
* `artifact`, the server first resolves WHICH authorities govern this context (from its own
|
|
86
|
+
* signed registrar chain — never from anything you present), asks every one of them, and
|
|
87
|
+
* combines strictly. Returns a CanResolutionResult instead of a CanResult. Keyless. */
|
|
88
|
+
context?: Record<string, unknown>;
|
|
83
89
|
/** Flat-facts convenience: any extra keys are treated as inputs when `inputs` is omitted. */
|
|
84
90
|
[key: string]: unknown;
|
|
85
91
|
}
|
|
92
|
+
/** One governing authority the resolver found for a context (NOMOS-SPEC-014). */
|
|
93
|
+
interface ResolvedAuthorityRef {
|
|
94
|
+
authority: string;
|
|
95
|
+
version: string | null;
|
|
96
|
+
registrar: string;
|
|
97
|
+
scope: string;
|
|
98
|
+
}
|
|
99
|
+
/** One resolved authority's answer inside a resolution-rail can() call. `verdict` is null with
|
|
100
|
+
* `unavailable` set when the member could not be consulted — which holds the WHOLE combined
|
|
101
|
+
* answer open (INSUFFICIENT_DATA), never acts as a permit. */
|
|
102
|
+
interface CanResolutionMember {
|
|
103
|
+
authority: string;
|
|
104
|
+
verdict: string | null;
|
|
105
|
+
unavailable: string | null;
|
|
106
|
+
query_id?: string | null;
|
|
107
|
+
rule?: string | null;
|
|
108
|
+
reason?: string | null;
|
|
109
|
+
escalation_id?: string | null;
|
|
110
|
+
revoked?: boolean;
|
|
111
|
+
missing_facts?: string[];
|
|
112
|
+
}
|
|
113
|
+
/** The resolution rail's answer (NOMOS-SPEC-014): which authorities govern, each one's own
|
|
114
|
+
* sealed answer, and the strict combination. `verdict` adds two values CanResult never has:
|
|
115
|
+
* INSUFFICIENT_DATA (resolution or a member held open — named in `open_facts`/`resolution`)
|
|
116
|
+
* and CONFLICT (two registrars assert exclusive precedence; a person settles it). */
|
|
117
|
+
interface CanResolutionResult {
|
|
118
|
+
mode: 'resolution';
|
|
119
|
+
allowed: boolean;
|
|
120
|
+
verdict: 'AUTHORIZED' | 'DENIED' | 'ESCALATED' | 'INSUFFICIENT_DATA' | 'CONFLICT';
|
|
121
|
+
reason: string;
|
|
122
|
+
resolution: {
|
|
123
|
+
status: 'RESOLVED' | 'CONFLICT' | 'INCOMPLETE';
|
|
124
|
+
applicable?: ResolvedAuthorityRef[];
|
|
125
|
+
missing?: {
|
|
126
|
+
context_facts: string[];
|
|
127
|
+
unreachable_registrars: string[];
|
|
128
|
+
stale_closures: string[];
|
|
129
|
+
/** SPEC-014 0.2.0: a current closure names law the registrar's log no longer claims, or the reverse */
|
|
130
|
+
closure_disagreements?: Array<{
|
|
131
|
+
registrar: string;
|
|
132
|
+
authority: string;
|
|
133
|
+
reason: 'closed_but_not_claimed' | 'claimed_but_not_closed';
|
|
134
|
+
}>;
|
|
135
|
+
/** SPEC-014 0.2.0: registrars whose log copy is older than the relying party's bound */
|
|
136
|
+
stale_heads?: string[];
|
|
137
|
+
};
|
|
138
|
+
conflicts?: unknown[];
|
|
139
|
+
proof: unknown;
|
|
140
|
+
};
|
|
141
|
+
members: CanResolutionMember[];
|
|
142
|
+
deciding_authorities: string[];
|
|
143
|
+
escalations: Array<{
|
|
144
|
+
authority: string;
|
|
145
|
+
escalation_id: string | null;
|
|
146
|
+
}>;
|
|
147
|
+
open_facts: string[];
|
|
148
|
+
/** The laws this answer was computed under (the resolved set). */
|
|
149
|
+
answered_under?: string[];
|
|
150
|
+
/** What the answer covers, in words: the registrar lists only its own authorities, so other law
|
|
151
|
+
* may also apply. An empty set is never AUTHORIZED. */
|
|
152
|
+
coverage?: string;
|
|
153
|
+
}
|
|
86
154
|
interface CanOptions {
|
|
87
155
|
/** Override the API host (defaults to the public NOMOS Exchange). */
|
|
88
156
|
baseUrl?: string;
|
|
@@ -836,6 +904,12 @@ declare class Nomos {
|
|
|
836
904
|
can(params: CanParams, opts?: CanOptions): Promise<CanResult>;
|
|
837
905
|
}
|
|
838
906
|
|
|
907
|
+
declare function can(params: CanParams & {
|
|
908
|
+
context: Record<string, unknown>;
|
|
909
|
+
authority?: undefined;
|
|
910
|
+
artifact_id?: undefined;
|
|
911
|
+
artifact?: undefined;
|
|
912
|
+
}, opts?: CanOptions): Promise<CanResolutionResult>;
|
|
839
913
|
declare function can(params: CanParams, opts?: CanOptions): Promise<CanResult>;
|
|
840
914
|
/**
|
|
841
915
|
* Ready-to-use, keyless entry point matching the canonical `nomos.can(...)` form.
|
|
@@ -1676,4 +1750,4 @@ declare function approver(o: ApproverOptions): {
|
|
|
1676
1750
|
pending: Map<string, Pending>;
|
|
1677
1751
|
};
|
|
1678
1752
|
|
|
1679
|
-
export { ACT_MEDIA_TYPE, type AcceptOptions, type AcceptStore, type AcceptTransaction, type AcceptedAuthority, type KeyCertificate as ActKeyCertificate, type ActOptions, type ActResult, type ActStatus, type Action, type Answer, type ApproverOptions, type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type AwaitApprovalOptions, type BindingProposal, type BindingSuggestion, type BuildActOptions, type CanApproval, type CanCaller, type CanOptions, type CanParams, type CanResult, type CanVerdict, type CertificateRegistry, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type EnrolmentClaims, type FactBinding, type GenerateGovernanceParams, type GenerateGovernanceResult, type IssueOptions, type IssuerDocument, type IssuerOptions, type KeyCertificate$1 as KeyCertificate, type ListArtifactsParams, MAX_PRESENTED, Nomos, NomosAPIError, NomosActError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosDiscoveryError, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type PasskeyApproverOptions, type PasskeyCredential, type PasskeyStore, type PendingApproval, type PgClient, type PgPool, type PolicyField, type PostgresStoreOptions, type Receipt, RegistryError, type RegistryStore, type RelyingPartyDocument, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type RevocationSourceOptions, RevocationUnavailable, type SignatureSuite, type Signer, type ToolBindings, type ToolDescription, type UnresolvedFact, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, WELL_KNOWN, type WithLastResponse, type WitnessDocument, type WitnessOptions, accept, act, applyToolBindings, approver, buildAct, can, certificateRegistry, certify, describeSuggestion, fetchDocument, generateSigner, issuer, kidOf, localSigner, memoryPasskeyStore, memoryRegistryStore, memoryStore, nomos, originOf, parseToolBindings, passkeyApprover, postgresPasskeyStore, postgresRegistryStore, postgresStore, presentedCertificates, readAttestation, reliedKids, revocationListCertificates, revocationSource, seal, seededSigner, signConsent, signEnrolmentToken, signInForce, signRevocation, signRevocationList, signTestimony, statusFor, suggestToolBindings, verifyChain, verifyEnrolmentToken, witness };
|
|
1753
|
+
export { ACT_MEDIA_TYPE, type AcceptOptions, type AcceptStore, type AcceptTransaction, type AcceptedAuthority, type KeyCertificate as ActKeyCertificate, type ActOptions, type ActResult, type ActStatus, type Action, type Answer, type ApproverOptions, type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type AwaitApprovalOptions, type BindingProposal, type BindingSuggestion, type BuildActOptions, type CanApproval, type CanCaller, type CanOptions, type CanParams, type CanResolutionMember, type CanResolutionResult, type CanResult, type CanVerdict, type CertificateRegistry, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type EnrolmentClaims, type FactBinding, type GenerateGovernanceParams, type GenerateGovernanceResult, type IssueOptions, type IssuerDocument, type IssuerOptions, type KeyCertificate$1 as KeyCertificate, type ListArtifactsParams, MAX_PRESENTED, Nomos, NomosAPIError, NomosActError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosDiscoveryError, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type PasskeyApproverOptions, type PasskeyCredential, type PasskeyStore, type PendingApproval, type PgClient, type PgPool, type PolicyField, type PostgresStoreOptions, type Receipt, RegistryError, type RegistryStore, type RelyingPartyDocument, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type ResolvedAuthorityRef, type RevocationSourceOptions, RevocationUnavailable, type SignatureSuite, type Signer, type ToolBindings, type ToolDescription, type UnresolvedFact, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, WELL_KNOWN, type WithLastResponse, type WitnessDocument, type WitnessOptions, accept, act, applyToolBindings, approver, buildAct, can, certificateRegistry, certify, describeSuggestion, fetchDocument, generateSigner, issuer, kidOf, localSigner, memoryPasskeyStore, memoryRegistryStore, memoryStore, nomos, originOf, parseToolBindings, passkeyApprover, postgresPasskeyStore, postgresRegistryStore, postgresStore, presentedCertificates, readAttestation, reliedKids, revocationListCertificates, revocationSource, seal, seededSigner, signConsent, signEnrolmentToken, signInForce, signRevocation, signRevocationList, signTestimony, statusFor, suggestToolBindings, verifyChain, verifyEnrolmentToken, witness };
|
package/dist/index.d.ts
CHANGED
|
@@ -80,9 +80,77 @@ interface CanParams {
|
|
|
80
80
|
/** The facts to evaluate. Either pass this explicitly, or spread the facts flat alongside
|
|
81
81
|
* `authority`/`action` — both shapes are accepted (explicit `inputs` wins if both given). */
|
|
82
82
|
inputs?: Record<string, unknown>;
|
|
83
|
+
/** Resolution rail (NOMOS-SPEC-014 Draft): the properties that attach law to an act —
|
|
84
|
+
* e.g. { jurisdiction: 'EU', sector: 'ai' }. With `context` and NO `authority`/`artifact_id`/
|
|
85
|
+
* `artifact`, the server first resolves WHICH authorities govern this context (from its own
|
|
86
|
+
* signed registrar chain — never from anything you present), asks every one of them, and
|
|
87
|
+
* combines strictly. Returns a CanResolutionResult instead of a CanResult. Keyless. */
|
|
88
|
+
context?: Record<string, unknown>;
|
|
83
89
|
/** Flat-facts convenience: any extra keys are treated as inputs when `inputs` is omitted. */
|
|
84
90
|
[key: string]: unknown;
|
|
85
91
|
}
|
|
92
|
+
/** One governing authority the resolver found for a context (NOMOS-SPEC-014). */
|
|
93
|
+
interface ResolvedAuthorityRef {
|
|
94
|
+
authority: string;
|
|
95
|
+
version: string | null;
|
|
96
|
+
registrar: string;
|
|
97
|
+
scope: string;
|
|
98
|
+
}
|
|
99
|
+
/** One resolved authority's answer inside a resolution-rail can() call. `verdict` is null with
|
|
100
|
+
* `unavailable` set when the member could not be consulted — which holds the WHOLE combined
|
|
101
|
+
* answer open (INSUFFICIENT_DATA), never acts as a permit. */
|
|
102
|
+
interface CanResolutionMember {
|
|
103
|
+
authority: string;
|
|
104
|
+
verdict: string | null;
|
|
105
|
+
unavailable: string | null;
|
|
106
|
+
query_id?: string | null;
|
|
107
|
+
rule?: string | null;
|
|
108
|
+
reason?: string | null;
|
|
109
|
+
escalation_id?: string | null;
|
|
110
|
+
revoked?: boolean;
|
|
111
|
+
missing_facts?: string[];
|
|
112
|
+
}
|
|
113
|
+
/** The resolution rail's answer (NOMOS-SPEC-014): which authorities govern, each one's own
|
|
114
|
+
* sealed answer, and the strict combination. `verdict` adds two values CanResult never has:
|
|
115
|
+
* INSUFFICIENT_DATA (resolution or a member held open — named in `open_facts`/`resolution`)
|
|
116
|
+
* and CONFLICT (two registrars assert exclusive precedence; a person settles it). */
|
|
117
|
+
interface CanResolutionResult {
|
|
118
|
+
mode: 'resolution';
|
|
119
|
+
allowed: boolean;
|
|
120
|
+
verdict: 'AUTHORIZED' | 'DENIED' | 'ESCALATED' | 'INSUFFICIENT_DATA' | 'CONFLICT';
|
|
121
|
+
reason: string;
|
|
122
|
+
resolution: {
|
|
123
|
+
status: 'RESOLVED' | 'CONFLICT' | 'INCOMPLETE';
|
|
124
|
+
applicable?: ResolvedAuthorityRef[];
|
|
125
|
+
missing?: {
|
|
126
|
+
context_facts: string[];
|
|
127
|
+
unreachable_registrars: string[];
|
|
128
|
+
stale_closures: string[];
|
|
129
|
+
/** SPEC-014 0.2.0: a current closure names law the registrar's log no longer claims, or the reverse */
|
|
130
|
+
closure_disagreements?: Array<{
|
|
131
|
+
registrar: string;
|
|
132
|
+
authority: string;
|
|
133
|
+
reason: 'closed_but_not_claimed' | 'claimed_but_not_closed';
|
|
134
|
+
}>;
|
|
135
|
+
/** SPEC-014 0.2.0: registrars whose log copy is older than the relying party's bound */
|
|
136
|
+
stale_heads?: string[];
|
|
137
|
+
};
|
|
138
|
+
conflicts?: unknown[];
|
|
139
|
+
proof: unknown;
|
|
140
|
+
};
|
|
141
|
+
members: CanResolutionMember[];
|
|
142
|
+
deciding_authorities: string[];
|
|
143
|
+
escalations: Array<{
|
|
144
|
+
authority: string;
|
|
145
|
+
escalation_id: string | null;
|
|
146
|
+
}>;
|
|
147
|
+
open_facts: string[];
|
|
148
|
+
/** The laws this answer was computed under (the resolved set). */
|
|
149
|
+
answered_under?: string[];
|
|
150
|
+
/** What the answer covers, in words: the registrar lists only its own authorities, so other law
|
|
151
|
+
* may also apply. An empty set is never AUTHORIZED. */
|
|
152
|
+
coverage?: string;
|
|
153
|
+
}
|
|
86
154
|
interface CanOptions {
|
|
87
155
|
/** Override the API host (defaults to the public NOMOS Exchange). */
|
|
88
156
|
baseUrl?: string;
|
|
@@ -836,6 +904,12 @@ declare class Nomos {
|
|
|
836
904
|
can(params: CanParams, opts?: CanOptions): Promise<CanResult>;
|
|
837
905
|
}
|
|
838
906
|
|
|
907
|
+
declare function can(params: CanParams & {
|
|
908
|
+
context: Record<string, unknown>;
|
|
909
|
+
authority?: undefined;
|
|
910
|
+
artifact_id?: undefined;
|
|
911
|
+
artifact?: undefined;
|
|
912
|
+
}, opts?: CanOptions): Promise<CanResolutionResult>;
|
|
839
913
|
declare function can(params: CanParams, opts?: CanOptions): Promise<CanResult>;
|
|
840
914
|
/**
|
|
841
915
|
* Ready-to-use, keyless entry point matching the canonical `nomos.can(...)` form.
|
|
@@ -1676,4 +1750,4 @@ declare function approver(o: ApproverOptions): {
|
|
|
1676
1750
|
pending: Map<string, Pending>;
|
|
1677
1751
|
};
|
|
1678
1752
|
|
|
1679
|
-
export { ACT_MEDIA_TYPE, type AcceptOptions, type AcceptStore, type AcceptTransaction, type AcceptedAuthority, type KeyCertificate as ActKeyCertificate, type ActOptions, type ActResult, type ActStatus, type Action, type Answer, type ApproverOptions, type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type AwaitApprovalOptions, type BindingProposal, type BindingSuggestion, type BuildActOptions, type CanApproval, type CanCaller, type CanOptions, type CanParams, type CanResult, type CanVerdict, type CertificateRegistry, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type EnrolmentClaims, type FactBinding, type GenerateGovernanceParams, type GenerateGovernanceResult, type IssueOptions, type IssuerDocument, type IssuerOptions, type KeyCertificate$1 as KeyCertificate, type ListArtifactsParams, MAX_PRESENTED, Nomos, NomosAPIError, NomosActError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosDiscoveryError, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type PasskeyApproverOptions, type PasskeyCredential, type PasskeyStore, type PendingApproval, type PgClient, type PgPool, type PolicyField, type PostgresStoreOptions, type Receipt, RegistryError, type RegistryStore, type RelyingPartyDocument, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type RevocationSourceOptions, RevocationUnavailable, type SignatureSuite, type Signer, type ToolBindings, type ToolDescription, type UnresolvedFact, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, WELL_KNOWN, type WithLastResponse, type WitnessDocument, type WitnessOptions, accept, act, applyToolBindings, approver, buildAct, can, certificateRegistry, certify, describeSuggestion, fetchDocument, generateSigner, issuer, kidOf, localSigner, memoryPasskeyStore, memoryRegistryStore, memoryStore, nomos, originOf, parseToolBindings, passkeyApprover, postgresPasskeyStore, postgresRegistryStore, postgresStore, presentedCertificates, readAttestation, reliedKids, revocationListCertificates, revocationSource, seal, seededSigner, signConsent, signEnrolmentToken, signInForce, signRevocation, signRevocationList, signTestimony, statusFor, suggestToolBindings, verifyChain, verifyEnrolmentToken, witness };
|
|
1753
|
+
export { ACT_MEDIA_TYPE, type AcceptOptions, type AcceptStore, type AcceptTransaction, type AcceptedAuthority, type KeyCertificate as ActKeyCertificate, type ActOptions, type ActResult, type ActStatus, type Action, type Answer, type ApproverOptions, type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type AwaitApprovalOptions, type BindingProposal, type BindingSuggestion, type BuildActOptions, type CanApproval, type CanCaller, type CanOptions, type CanParams, type CanResolutionMember, type CanResolutionResult, type CanResult, type CanVerdict, type CertificateRegistry, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type EnrolmentClaims, type FactBinding, type GenerateGovernanceParams, type GenerateGovernanceResult, type IssueOptions, type IssuerDocument, type IssuerOptions, type KeyCertificate$1 as KeyCertificate, type ListArtifactsParams, MAX_PRESENTED, Nomos, NomosAPIError, NomosActError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosDiscoveryError, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type PasskeyApproverOptions, type PasskeyCredential, type PasskeyStore, type PendingApproval, type PgClient, type PgPool, type PolicyField, type PostgresStoreOptions, type Receipt, RegistryError, type RegistryStore, type RelyingPartyDocument, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type ResolvedAuthorityRef, type RevocationSourceOptions, RevocationUnavailable, type SignatureSuite, type Signer, type ToolBindings, type ToolDescription, type UnresolvedFact, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, WELL_KNOWN, type WithLastResponse, type WitnessDocument, type WitnessOptions, accept, act, applyToolBindings, approver, buildAct, can, certificateRegistry, certify, describeSuggestion, fetchDocument, generateSigner, issuer, kidOf, localSigner, memoryPasskeyStore, memoryRegistryStore, memoryStore, nomos, originOf, parseToolBindings, passkeyApprover, postgresPasskeyStore, postgresRegistryStore, postgresStore, presentedCertificates, readAttestation, reliedKids, revocationListCertificates, revocationSource, seal, seededSigner, signConsent, signEnrolmentToken, signInForce, signRevocation, signRevocationList, signTestimony, statusFor, suggestToolBindings, verifyChain, verifyEnrolmentToken, witness };
|
package/dist/index.js
CHANGED
|
@@ -107,7 +107,7 @@ function isRetryable(status) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// src/http-client.ts
|
|
110
|
-
var SDK_VERSION = "2.
|
|
110
|
+
var SDK_VERSION = "2.6.0";
|
|
111
111
|
var HttpClient = class {
|
|
112
112
|
constructor(opts) {
|
|
113
113
|
this.opts = opts;
|
|
@@ -774,6 +774,50 @@ async function canViaChain(artifact, keyCerts, rootPublicKeyPem, facts, action,
|
|
|
774
774
|
if (data?.decision === "MALFORMED") throw new NomosValidationError(message, data, requestId);
|
|
775
775
|
mapHttpError(res.status, data, requestId);
|
|
776
776
|
}
|
|
777
|
+
function normalizeResolutionResult(data) {
|
|
778
|
+
return {
|
|
779
|
+
mode: "resolution",
|
|
780
|
+
allowed: data.verdict === "AUTHORIZED",
|
|
781
|
+
verdict: data.verdict,
|
|
782
|
+
reason: data.reason ?? "",
|
|
783
|
+
resolution: data.resolution ?? { status: "INCOMPLETE", proof: null },
|
|
784
|
+
members: Array.isArray(data.members) ? data.members : [],
|
|
785
|
+
deciding_authorities: Array.isArray(data.deciding_authorities) ? data.deciding_authorities : [],
|
|
786
|
+
escalations: Array.isArray(data.escalations) ? data.escalations : [],
|
|
787
|
+
open_facts: Array.isArray(data.open_facts) ? data.open_facts : [],
|
|
788
|
+
...Array.isArray(data.answered_under) ? { answered_under: data.answered_under } : {},
|
|
789
|
+
...typeof data.coverage === "string" ? { coverage: data.coverage } : {}
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
function resolutionBody(params) {
|
|
793
|
+
const { authority, artifact_id, artifact, key_certs, root_public_key_pem, action, inputs, context, ...flat } = params;
|
|
794
|
+
void authority;
|
|
795
|
+
void artifact_id;
|
|
796
|
+
void artifact;
|
|
797
|
+
void key_certs;
|
|
798
|
+
void root_public_key_pem;
|
|
799
|
+
const facts = inputs && typeof inputs === "object" ? inputs : flat;
|
|
800
|
+
return { context, ...action ? { action } : {}, ...facts };
|
|
801
|
+
}
|
|
802
|
+
async function canViaResolution(params, opts) {
|
|
803
|
+
const base = (opts?.baseUrl ?? DEFAULT_EXCHANGE_BASE).replace(/\/$/, "");
|
|
804
|
+
let res;
|
|
805
|
+
try {
|
|
806
|
+
res = await fetch(`${base}/api/v1/can`, {
|
|
807
|
+
method: "POST",
|
|
808
|
+
headers: { "Content-Type": "application/json", "User-Agent": `nomos-node/${SDK_VERSION}`, ...callerHeaders(opts?.caller) },
|
|
809
|
+
body: JSON.stringify(resolutionBody(params)),
|
|
810
|
+
signal: opts?.signal
|
|
811
|
+
});
|
|
812
|
+
} catch (err) {
|
|
813
|
+
if (err?.name === "AbortError") throw new NomosNetworkError("Request aborted");
|
|
814
|
+
throw new NomosNetworkError(err?.message ?? "Network error");
|
|
815
|
+
}
|
|
816
|
+
const requestId = res.headers.get("x-request-id") ?? "";
|
|
817
|
+
const data = await res.json().catch(() => null);
|
|
818
|
+
if (!res.ok) mapHttpError(res.status, data, requestId);
|
|
819
|
+
return normalizeResolutionResult(data);
|
|
820
|
+
}
|
|
777
821
|
async function can(params, opts) {
|
|
778
822
|
if (params.artifact !== void 0) {
|
|
779
823
|
if (!params.key_certs || !params.root_public_key_pem) {
|
|
@@ -785,8 +829,11 @@ async function can(params, opts) {
|
|
|
785
829
|
if (params.artifact_id) {
|
|
786
830
|
throw new Error("can(): `artifact_id` requires an API key \u2014 use `client.can(...)` on an authenticated Nomos instance, not the free `can()` export.");
|
|
787
831
|
}
|
|
832
|
+
if (!params.authority && params.context && typeof params.context === "object" && !Array.isArray(params.context)) {
|
|
833
|
+
return canViaResolution(params, opts);
|
|
834
|
+
}
|
|
788
835
|
if (!params.authority || typeof params.authority !== "string") {
|
|
789
|
-
throw new Error("can(): `authority` (an Exchange artifact id or slug), `artifact_id` (via client.can()),
|
|
836
|
+
throw new Error("can(): `authority` (an Exchange artifact id or slug), `artifact_id` (via client.can()), `artifact` (chain-of-trust), or `context` (resolution, NOMOS-SPEC-014) is required.");
|
|
790
837
|
}
|
|
791
838
|
const { facts, action } = splitFacts(params);
|
|
792
839
|
return canViaExchange(params.authority, facts, action, opts);
|
|
@@ -801,7 +848,11 @@ async function canAuthenticated(http, params, opts) {
|
|
|
801
848
|
}
|
|
802
849
|
if (params.artifact_id) return canViaArtifact(http, params.artifact_id, facts, action);
|
|
803
850
|
if (params.authority) return canViaExchangeAuthenticated(http, params.authority, facts, action, opts);
|
|
804
|
-
|
|
851
|
+
if (params.context && typeof params.context === "object" && !Array.isArray(params.context)) {
|
|
852
|
+
const data = (await http.post("/api/v1/can", resolutionBody(params), { headers: callerHeaders(opts?.caller) })).data;
|
|
853
|
+
return normalizeResolutionResult(data);
|
|
854
|
+
}
|
|
855
|
+
throw new Error("can(): `authority` (a public Exchange artifact), `artifact_id` (your own artifact), `artifact` (chain-of-trust), or `context` (resolution, NOMOS-SPEC-014) is required.");
|
|
805
856
|
}
|
|
806
857
|
var nomos = { can };
|
|
807
858
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nomosprotocol/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "NOMOS for TypeScript: accept() for the system that makes a change, act() for the agent that asks, the issuer, witness and approver roles, and the Exchange client. Verification is local (@nomosprotocol/verify).",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|