@atlasent/sdk 1.5.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/LICENSE +190 -0
- package/README.md +133 -0
- package/dist/behavior.cjs +175 -0
- package/dist/behavior.cjs.map +1 -0
- package/dist/behavior.d.cts +241 -0
- package/dist/behavior.d.ts +241 -0
- package/dist/behavior.js +143 -0
- package/dist/behavior.js.map +1 -0
- package/dist/hono.cjs +580 -0
- package/dist/hono.cjs.map +1 -0
- package/dist/hono.d.cts +109 -0
- package/dist/hono.d.ts +109 -0
- package/dist/hono.js +550 -0
- package/dist/hono.js.map +1 -0
- package/dist/index.cjs +763 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +620 -0
- package/dist/index.d.ts +620 -0
- package/dist/index.js +723 -0
- package/dist/index.js.map +1 -0
- package/dist/protect-BKxcoR_2.d.cts +159 -0
- package/dist/protect-BKxcoR_2.d.ts +159 -0
- package/package.json +101 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
import { webcrypto } from 'node:crypto';
|
|
2
|
+
import { p as protect, c as configure, a as AtlaSentError, A as AtlaSentDeniedError } from './protect-BKxcoR_2.js';
|
|
3
|
+
export { d as AtlaSentDecision, e as AtlaSentDeniedErrorInit, f as AtlaSentErrorCode, g as AtlaSentErrorInit, C as ConfigureOptions, P as Permit, b as ProtectRequest } from './protect-BKxcoR_2.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shared audit wire types — the `/v1/audit/*` HTTP surface.
|
|
7
|
+
*
|
|
8
|
+
* Source of truth: `atlasent-api/supabase/functions/v1-audit/index.ts`
|
|
9
|
+
* (the edge function that serves `GET /v1/audit/events`,
|
|
10
|
+
* `POST /v1/audit/exports`, and `POST /v1/audit/verify`). The docstring
|
|
11
|
+
* at the top of that file describes these shapes. Keep this module in
|
|
12
|
+
* lockstep with it; `test/audit-types.test.ts` contains type-level
|
|
13
|
+
* assertions against the field set to make drift obvious.
|
|
14
|
+
*
|
|
15
|
+
* Fields are snake_case because that is what the server emits on the
|
|
16
|
+
* wire — unlike the evaluate / verify-permit types in `./types.ts`,
|
|
17
|
+
* which use camelCase on the SDK side and translate at the client
|
|
18
|
+
* boundary, the audit surface is intentionally wire-identical so that
|
|
19
|
+
* signed export bundles round-trip byte-for-byte through verifiers.
|
|
20
|
+
*/
|
|
21
|
+
/** Policy decision enum used on `audit_events.decision`. */
|
|
22
|
+
type AuditDecision = 'allow' | 'deny' | 'hold' | 'escalate';
|
|
23
|
+
/**
|
|
24
|
+
* Signing status reported on an export bundle. "signed" is the normal
|
|
25
|
+
* path; "unsigned" means the deployment has no active signing key;
|
|
26
|
+
* "signing_failed" means a key is configured but signing errored and
|
|
27
|
+
* the export was returned anyway (the signature is an empty string).
|
|
28
|
+
*/
|
|
29
|
+
type AuditExportSignatureStatus = 'signed' | 'unsigned' | 'signing_failed';
|
|
30
|
+
/**
|
|
31
|
+
* One persisted row from `audit_events`, as returned by
|
|
32
|
+
* `GET /v1/audit/events` and embedded inside `AuditExport.events`.
|
|
33
|
+
*
|
|
34
|
+
* `decision` is nullable because not every event is an evaluation —
|
|
35
|
+
* CRUD-style audit writes (e.g. `policy.updated`) omit it. All other
|
|
36
|
+
* nullable fields follow the same "field doesn't apply to this event
|
|
37
|
+
* type" convention rather than "unknown value".
|
|
38
|
+
*/
|
|
39
|
+
interface AuditEvent {
|
|
40
|
+
/** Event UUID. Stable; surfaces as `tampered_event_ids` on verify failure. */
|
|
41
|
+
id: string;
|
|
42
|
+
/** Organization this event belongs to. */
|
|
43
|
+
org_id: string;
|
|
44
|
+
/** Per-org monotonic sequence. Used as the pagination cursor's payload. */
|
|
45
|
+
sequence: number;
|
|
46
|
+
/** Event type tag (e.g. "evaluate.allow", "policy.updated"). */
|
|
47
|
+
type: string;
|
|
48
|
+
/** Policy decision when the event is an evaluation; `null` otherwise. */
|
|
49
|
+
decision: AuditDecision | null;
|
|
50
|
+
/** Actor id (user / API key / agent) when applicable. */
|
|
51
|
+
actor_id: string | null;
|
|
52
|
+
/** Optional resource tag — e.g. "policy". */
|
|
53
|
+
resource_type: string | null;
|
|
54
|
+
/** Optional resource id — paired with `resource_type`. */
|
|
55
|
+
resource_id: string | null;
|
|
56
|
+
/** Canonical JSON of the event payload. Hashed into `hash`. */
|
|
57
|
+
payload: Record<string, unknown> | null;
|
|
58
|
+
/** SHA-256(prev_hash || canonicalJSON(payload)), hex. */
|
|
59
|
+
hash: string;
|
|
60
|
+
/** Previous event's `hash` (genesis is `"0".repeat(64)`). */
|
|
61
|
+
previous_hash: string;
|
|
62
|
+
/** When the underlying action occurred (ISO 8601). */
|
|
63
|
+
occurred_at: string;
|
|
64
|
+
/** When the row was persisted (ISO 8601). */
|
|
65
|
+
created_at: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Response shape for `GET /v1/audit/events`.
|
|
69
|
+
*
|
|
70
|
+
* `total` is the filter's full count (not just this page) so callers
|
|
71
|
+
* can show "page 1 of N" without an extra HEAD request.
|
|
72
|
+
*
|
|
73
|
+
* `next_cursor` is an opaque base64url string. Pass it back verbatim
|
|
74
|
+
* as `?cursor=...` to fetch the next page. Absent when this is the
|
|
75
|
+
* last page.
|
|
76
|
+
*/
|
|
77
|
+
interface AuditEventsPage {
|
|
78
|
+
events: AuditEvent[];
|
|
79
|
+
total: number;
|
|
80
|
+
next_cursor?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Query parameters accepted by `GET /v1/audit/events`. Serialize as
|
|
84
|
+
* URL search params — `types` is a comma-joined list on the wire
|
|
85
|
+
* (e.g. `types=evaluate.allow,policy.updated`).
|
|
86
|
+
*
|
|
87
|
+
* All fields are optional; the server defaults `limit` to 50 and caps
|
|
88
|
+
* it at 500.
|
|
89
|
+
*/
|
|
90
|
+
interface AuditEventsQuery {
|
|
91
|
+
/** Comma-joined list of event types to filter on. */
|
|
92
|
+
types?: string;
|
|
93
|
+
/** Filter to a single actor. */
|
|
94
|
+
actor_id?: string;
|
|
95
|
+
/** Inclusive lower bound on `occurred_at` (ISO 8601). */
|
|
96
|
+
from?: string;
|
|
97
|
+
/** Inclusive upper bound on `occurred_at` (ISO 8601). */
|
|
98
|
+
to?: string;
|
|
99
|
+
/** Page size. Default 50, min 1, max 500. */
|
|
100
|
+
limit?: number;
|
|
101
|
+
/** Opaque cursor returned as `next_cursor` by the prior page. */
|
|
102
|
+
cursor?: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Response shape for `POST /v1/audit/exports` — a signed bundle of
|
|
106
|
+
* audit events suitable for offline verification.
|
|
107
|
+
*
|
|
108
|
+
* `signature` is detached Ed25519 over `signedBytesFor(bundle)` (see
|
|
109
|
+
* `./auditBundle.ts`). An empty string means the server attempted to
|
|
110
|
+
* sign but failed; check `signature_status` to distinguish.
|
|
111
|
+
*
|
|
112
|
+
* `tampered_event_ids` surfaces rows whose recomputed hash doesn't
|
|
113
|
+
* match the stored hash — even when `chain_integrity_ok` is false,
|
|
114
|
+
* the signature still covers whatever the server emitted, so callers
|
|
115
|
+
* that trust the signature must still inspect this list.
|
|
116
|
+
*/
|
|
117
|
+
interface AuditExport {
|
|
118
|
+
/** Server-assigned UUID for this export. */
|
|
119
|
+
export_id: string;
|
|
120
|
+
/** Organization the bundle belongs to. */
|
|
121
|
+
org_id: string;
|
|
122
|
+
/** Events in canonical (ascending sequence) order. */
|
|
123
|
+
events: AuditEvent[];
|
|
124
|
+
/** Last event's `hash`, or `"0".repeat(64)` if `events` is empty. */
|
|
125
|
+
chain_head_hash: string;
|
|
126
|
+
/** `true` iff adjacency + re-hash succeeded for every event. */
|
|
127
|
+
chain_integrity_ok: boolean;
|
|
128
|
+
/** `AuditEvent.id`s whose recomputed hash != stored hash. */
|
|
129
|
+
tampered_event_ids: string[];
|
|
130
|
+
/** Detached Ed25519 signature (base64url). Empty string on sign failure. */
|
|
131
|
+
signature: string;
|
|
132
|
+
/** Outcome of the signing attempt. */
|
|
133
|
+
signature_status: AuditExportSignatureStatus;
|
|
134
|
+
/** Registry id of the key that signed — absent when unsigned. */
|
|
135
|
+
signing_key_id?: string;
|
|
136
|
+
/** When the bundle was signed (ISO 8601). */
|
|
137
|
+
signed_at: string;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Public types for the AtlaSent TypeScript SDK.
|
|
142
|
+
*
|
|
143
|
+
* These shapes are deliberately minimal and 1:1 with the AtlaSent
|
|
144
|
+
* authorization API. Request / response fields are camelCase on the
|
|
145
|
+
* SDK side; the client handles snake_case translation on the wire.
|
|
146
|
+
*/
|
|
147
|
+
|
|
148
|
+
/** The two possible policy decisions. */
|
|
149
|
+
type Decision = "ALLOW" | "DENY";
|
|
150
|
+
/**
|
|
151
|
+
* Rate-limit state parsed from the server's `X-RateLimit-*` headers.
|
|
152
|
+
*
|
|
153
|
+
* Present on every authenticated response (success and 429) when the
|
|
154
|
+
* server emits the headers. `null` when the server doesn't — older
|
|
155
|
+
* deployments, or internal endpoints that skip per-key rate limiting.
|
|
156
|
+
*
|
|
157
|
+
* Clients should check `remaining` and sleep until `resetAt` to
|
|
158
|
+
* preemptively back off before hitting a 429.
|
|
159
|
+
*/
|
|
160
|
+
interface RateLimitState {
|
|
161
|
+
/** Value of `X-RateLimit-Limit` — the per-minute budget. */
|
|
162
|
+
limit: number;
|
|
163
|
+
/** Value of `X-RateLimit-Remaining` — unused budget in the current window. */
|
|
164
|
+
remaining: number;
|
|
165
|
+
/**
|
|
166
|
+
* Parsed `X-RateLimit-Reset` — the UTC instant when the current
|
|
167
|
+
* window's counter zeroes. Accepts either a unix-seconds integer or
|
|
168
|
+
* an ISO 8601 string on the wire.
|
|
169
|
+
*/
|
|
170
|
+
resetAt: Date;
|
|
171
|
+
}
|
|
172
|
+
/** Input to {@link AtlaSentClient.evaluate}. */
|
|
173
|
+
interface EvaluateRequest {
|
|
174
|
+
/** Identifier of the calling agent (e.g. "clinical-data-agent"). */
|
|
175
|
+
agent: string;
|
|
176
|
+
/** The action being authorized (e.g. "modify_patient_record"). */
|
|
177
|
+
action: string;
|
|
178
|
+
/** Arbitrary policy context (user, environment, resource IDs). */
|
|
179
|
+
context?: Record<string, unknown>;
|
|
180
|
+
}
|
|
181
|
+
/** Result of {@link AtlaSentClient.evaluate}. */
|
|
182
|
+
interface EvaluateResponse {
|
|
183
|
+
/** "ALLOW" or "DENY". A "DENY" is not thrown — branch on this field. */
|
|
184
|
+
decision: Decision;
|
|
185
|
+
/** Opaque permit identifier, passed to {@link AtlaSentClient.verifyPermit}. */
|
|
186
|
+
permitId: string;
|
|
187
|
+
/** Human-readable explanation from the policy engine. */
|
|
188
|
+
reason: string;
|
|
189
|
+
/** Hash-chained audit-trail entry (21 CFR Part 11 / GxP-ready). */
|
|
190
|
+
auditHash: string;
|
|
191
|
+
/** ISO 8601 timestamp of the decision. */
|
|
192
|
+
timestamp: string;
|
|
193
|
+
/**
|
|
194
|
+
* Per-key rate-limit state for this request's response, parsed from
|
|
195
|
+
* `X-RateLimit-*` headers. `null` when the server didn't emit them.
|
|
196
|
+
*/
|
|
197
|
+
rateLimit: RateLimitState | null;
|
|
198
|
+
}
|
|
199
|
+
/** Input to {@link AtlaSentClient.verifyPermit}. */
|
|
200
|
+
interface VerifyPermitRequest {
|
|
201
|
+
/** The permit ID returned by a prior evaluate() call. */
|
|
202
|
+
permitId: string;
|
|
203
|
+
/** Optional: re-state the action for cross-check with the server. */
|
|
204
|
+
action?: string;
|
|
205
|
+
/** Optional: re-state the agent for cross-check with the server. */
|
|
206
|
+
agent?: string;
|
|
207
|
+
/** Optional: re-state the context for cross-check with the server. */
|
|
208
|
+
context?: Record<string, unknown>;
|
|
209
|
+
}
|
|
210
|
+
/** Result of {@link AtlaSentClient.verifyPermit}. */
|
|
211
|
+
interface VerifyPermitResponse {
|
|
212
|
+
/** `true` when the permit is valid and un-revoked. */
|
|
213
|
+
verified: boolean;
|
|
214
|
+
/** Verification outcome string from the server. */
|
|
215
|
+
outcome: string;
|
|
216
|
+
/** Verification hash bound to the permit. */
|
|
217
|
+
permitHash: string;
|
|
218
|
+
/** ISO 8601 timestamp of the verification. */
|
|
219
|
+
timestamp: string;
|
|
220
|
+
/**
|
|
221
|
+
* Per-key rate-limit state for this request's response, parsed from
|
|
222
|
+
* `X-RateLimit-*` headers. `null` when the server didn't emit them.
|
|
223
|
+
*/
|
|
224
|
+
rateLimit: RateLimitState | null;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Result of {@link AtlaSentClient.keySelf} — self-introspection of the API
|
|
228
|
+
* key the client was constructed with. Returned by `GET /v1/api-key-self`.
|
|
229
|
+
*
|
|
230
|
+
* Never includes the raw key or its hash — introspection is intentionally
|
|
231
|
+
* read-only and safe to surface in operator dashboards. Useful for:
|
|
232
|
+
* - "which key am I?" debugging
|
|
233
|
+
* - IP_NOT_ALLOWED failures — `clientIp` is the IP the server observed
|
|
234
|
+
* - proactive expiry warnings — `expiresAt` is the server-stored expiry
|
|
235
|
+
* (`null` means the key does not auto-expire)
|
|
236
|
+
* - verifying scopes before attempting a scope-gated action
|
|
237
|
+
*/
|
|
238
|
+
interface ApiKeySelfResponse {
|
|
239
|
+
/** Server-side UUID of the api_keys row for this key. */
|
|
240
|
+
keyId: string;
|
|
241
|
+
/** Organization the key belongs to. */
|
|
242
|
+
organizationId: string;
|
|
243
|
+
/** "live" or "test" (or any future environment label the server introduces). */
|
|
244
|
+
environment: string;
|
|
245
|
+
/** Granted scopes — e.g. ["evaluate", "audit.read"]. */
|
|
246
|
+
scopes: string[];
|
|
247
|
+
/**
|
|
248
|
+
* Per-key IP allowlist as CIDR strings (e.g. ["10.0.0.0/8"]). `null`
|
|
249
|
+
* when the key is unrestricted.
|
|
250
|
+
*/
|
|
251
|
+
allowedCidrs: string[] | null;
|
|
252
|
+
/** Server-enforced per-minute rate limit for this key. */
|
|
253
|
+
rateLimitPerMinute: number;
|
|
254
|
+
/** Client IP as the server observed it (first hop of X-Forwarded-For). */
|
|
255
|
+
clientIp: string | null;
|
|
256
|
+
/** Server-stored expiry; `null` means the key does not auto-expire. */
|
|
257
|
+
expiresAt: string | null;
|
|
258
|
+
/**
|
|
259
|
+
* Per-key rate-limit state for this request's response, parsed from
|
|
260
|
+
* `X-RateLimit-*` headers. `null` when the server didn't emit them.
|
|
261
|
+
*/
|
|
262
|
+
rateLimit: RateLimitState | null;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Result of {@link AtlaSentClient.listAuditEvents}. Extends the raw
|
|
266
|
+
* wire page with a camelCase `rateLimit` alongside the snake_case
|
|
267
|
+
* wire fields — the wire shape (`events`, `total`, `next_cursor`) is
|
|
268
|
+
* untouched so callers that pass it to the offline verifier get
|
|
269
|
+
* byte-identical behaviour.
|
|
270
|
+
*/
|
|
271
|
+
interface AuditEventsResult extends AuditEventsPage {
|
|
272
|
+
/**
|
|
273
|
+
* Per-key rate-limit state for this request's response, parsed from
|
|
274
|
+
* `X-RateLimit-*` headers. `null` when the server didn't emit them.
|
|
275
|
+
*/
|
|
276
|
+
rateLimit: RateLimitState | null;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Filter accepted by {@link AtlaSentClient.createAuditExport}. Fields
|
|
280
|
+
* are snake_case to match the server's `POST /v1-audit/exports`
|
|
281
|
+
* request body; an empty object requests a full-org bundle.
|
|
282
|
+
*/
|
|
283
|
+
interface AuditExportRequest {
|
|
284
|
+
/** Comma-joined list of event types to include (e.g. `"evaluate.allow,policy.updated"`). */
|
|
285
|
+
types?: string;
|
|
286
|
+
/** Filter to a single actor. */
|
|
287
|
+
actor_id?: string;
|
|
288
|
+
/** Inclusive lower bound on `occurred_at` (ISO 8601). */
|
|
289
|
+
from?: string;
|
|
290
|
+
/** Inclusive upper bound on `occurred_at` (ISO 8601). */
|
|
291
|
+
to?: string;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Result of {@link AtlaSentClient.createAuditExport}. Extends the
|
|
295
|
+
* signed bundle shape with a camelCase `rateLimit`. The signed
|
|
296
|
+
* envelope fields (`export_id`, `org_id`, `chain_head_hash`,
|
|
297
|
+
* `event_count`, `signed_at`, `events`, `signature`) are preserved
|
|
298
|
+
* byte-for-byte so the object can be handed straight to
|
|
299
|
+
* `verifyAuditBundle(bundle, keys)`.
|
|
300
|
+
*/
|
|
301
|
+
interface AuditExportResult extends AuditExport {
|
|
302
|
+
/**
|
|
303
|
+
* Per-key rate-limit state for this request's response, parsed from
|
|
304
|
+
* `X-RateLimit-*` headers. `null` when the server didn't emit them.
|
|
305
|
+
*/
|
|
306
|
+
rateLimit: RateLimitState | null;
|
|
307
|
+
}
|
|
308
|
+
/** Constructor options for {@link AtlaSentClient}. */
|
|
309
|
+
interface AtlaSentClientOptions {
|
|
310
|
+
/** Required. Your AtlaSent API key. */
|
|
311
|
+
apiKey: string;
|
|
312
|
+
/** API base URL. Defaults to "https://api.atlasent.io". */
|
|
313
|
+
baseUrl?: string;
|
|
314
|
+
/** Per-request timeout in milliseconds. Defaults to 10_000. */
|
|
315
|
+
timeoutMs?: number;
|
|
316
|
+
/**
|
|
317
|
+
* Inject a fetch implementation (primarily for testing).
|
|
318
|
+
* Defaults to `globalThis.fetch`.
|
|
319
|
+
*/
|
|
320
|
+
fetch?: typeof fetch;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* AtlaSent HTTP client.
|
|
325
|
+
*
|
|
326
|
+
* Two public methods, both backed by native `fetch`:
|
|
327
|
+
* - {@link AtlaSentClient.evaluate} → POST {baseUrl}/v1-evaluate
|
|
328
|
+
* - {@link AtlaSentClient.verifyPermit} → POST {baseUrl}/v1-verify-permit
|
|
329
|
+
*
|
|
330
|
+
* Fail-closed: a clean policy DENY is returned (not thrown), but
|
|
331
|
+
* network, timeout, bad response, 4xx/5xx, and rate-limit conditions
|
|
332
|
+
* all throw {@link AtlaSentError}.
|
|
333
|
+
*/
|
|
334
|
+
|
|
335
|
+
declare class AtlaSentClient {
|
|
336
|
+
private readonly apiKey;
|
|
337
|
+
private readonly baseUrl;
|
|
338
|
+
private readonly timeoutMs;
|
|
339
|
+
private readonly fetchImpl;
|
|
340
|
+
constructor(options: AtlaSentClientOptions);
|
|
341
|
+
/**
|
|
342
|
+
* Ask the policy engine whether an agent action is permitted.
|
|
343
|
+
*
|
|
344
|
+
* A "DENY" is **not** thrown — it is returned in
|
|
345
|
+
* `response.decision`. Network errors, invalid API key, rate
|
|
346
|
+
* limits, timeouts, and malformed responses throw
|
|
347
|
+
* {@link AtlaSentError}.
|
|
348
|
+
*/
|
|
349
|
+
evaluate(input: EvaluateRequest): Promise<EvaluateResponse>;
|
|
350
|
+
/**
|
|
351
|
+
* Verify that a previously issued permit is still valid.
|
|
352
|
+
*
|
|
353
|
+
* A `verified: false` response is **not** thrown — inspect the
|
|
354
|
+
* returned object. Only transport / server errors throw.
|
|
355
|
+
*/
|
|
356
|
+
verifyPermit(input: VerifyPermitRequest): Promise<VerifyPermitResponse>;
|
|
357
|
+
/**
|
|
358
|
+
* Self-introspection: ask the server to describe the API key this
|
|
359
|
+
* client was constructed with. Returns the key's ID, organization,
|
|
360
|
+
* environment, scopes, IP allowlist, per-minute rate limit, the
|
|
361
|
+
* client IP the server observed, and the expiry (if any).
|
|
362
|
+
*
|
|
363
|
+
* Never includes the raw key or its hash. Safe to surface in operator
|
|
364
|
+
* dashboards. Useful for `IP_NOT_ALLOWED` debugging (the server tells
|
|
365
|
+
* you exactly which IP it saw) and for proactive expiry warnings.
|
|
366
|
+
*
|
|
367
|
+
* Throws {@link AtlaSentError} on transport / auth failures — same
|
|
368
|
+
* taxonomy as {@link AtlaSentClient.evaluate}.
|
|
369
|
+
*/
|
|
370
|
+
keySelf(): Promise<ApiKeySelfResponse>;
|
|
371
|
+
/**
|
|
372
|
+
* List persisted audit events for the authenticated organization
|
|
373
|
+
* (`GET /v1-audit/events`). Returned rows are wire-identical with
|
|
374
|
+
* the server: snake_case field names, including `previous_hash` and
|
|
375
|
+
* the `hash` chain, so the response can be fed straight into the
|
|
376
|
+
* offline verifier when paired with a signed export.
|
|
377
|
+
*
|
|
378
|
+
* `query.types` is a comma-joined list (e.g.
|
|
379
|
+
* `"evaluate.allow,policy.updated"`). `cursor` is the opaque
|
|
380
|
+
* `next_cursor` from the prior page. All fields are optional; the
|
|
381
|
+
* server defaults `limit` to 50 (capped at 500).
|
|
382
|
+
*
|
|
383
|
+
* Throws {@link AtlaSentError} on transport / auth failures — same
|
|
384
|
+
* taxonomy as {@link AtlaSentClient.evaluate}.
|
|
385
|
+
*/
|
|
386
|
+
listAuditEvents(query?: AuditEventsQuery): Promise<AuditEventsResult>;
|
|
387
|
+
/**
|
|
388
|
+
* Request a signed audit export bundle
|
|
389
|
+
* (`POST /v1-audit/exports`). The returned object is wire-identical
|
|
390
|
+
* with the server — `signature`, `chain_head_hash`, `events`, and
|
|
391
|
+
* friends survive untouched so the bundle can be persisted to disk
|
|
392
|
+
* and handed to the offline verifier (`verifyBundle` /
|
|
393
|
+
* `verifyAuditBundle`) without any reshaping.
|
|
394
|
+
*
|
|
395
|
+
* Pass `filter.types`, `filter.from`, `filter.to`, or `filter.actor_id`
|
|
396
|
+
* to narrow the export; omit for a full-org bundle. `rateLimit` is
|
|
397
|
+
* attached alongside the wire fields for observability.
|
|
398
|
+
*
|
|
399
|
+
* Throws {@link AtlaSentError} on transport / auth failures — same
|
|
400
|
+
* taxonomy as {@link AtlaSentClient.evaluate}.
|
|
401
|
+
*/
|
|
402
|
+
createAuditExport(filter?: AuditExportRequest): Promise<AuditExportResult>;
|
|
403
|
+
private post;
|
|
404
|
+
private get;
|
|
405
|
+
private request;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/** Node's webcrypto CryptoKey — kept local so the module doesn't depend on DOM types. */
|
|
409
|
+
type WebCryptoKey = webcrypto.CryptoKey;
|
|
410
|
+
/** Public key candidate the verifier will try, tagged with its registry id. */
|
|
411
|
+
interface VerifyKey {
|
|
412
|
+
keyId: string;
|
|
413
|
+
publicKey: WebCryptoKey;
|
|
414
|
+
}
|
|
415
|
+
interface BundleVerificationResult {
|
|
416
|
+
/**
|
|
417
|
+
* AND of three checks: adjacency (each event's `previous_hash`
|
|
418
|
+
* equals the prior event's `hash`), per-event hash recomputation
|
|
419
|
+
* from the canonical payload, and `chain_head_hash` matching the
|
|
420
|
+
* last event's stored hash.
|
|
421
|
+
*/
|
|
422
|
+
chainIntegrityOk: boolean;
|
|
423
|
+
/** Ed25519 signature verified against one of the supplied public keys. */
|
|
424
|
+
signatureValid: boolean;
|
|
425
|
+
/** `chain_head_hash` equals the last event's stored `hash`. */
|
|
426
|
+
headHashMatches: boolean;
|
|
427
|
+
/** Event ids whose recomputed hash != stored hash. */
|
|
428
|
+
tamperedEventIds: string[];
|
|
429
|
+
/** Which registry key id matched, when `signatureValid` is true. */
|
|
430
|
+
matchedKeyId?: string | undefined;
|
|
431
|
+
/** Non-fatal explanation when a flag is false. */
|
|
432
|
+
reason?: string | undefined;
|
|
433
|
+
/** Convenience: `chainIntegrityOk && signatureValid`. */
|
|
434
|
+
verified: boolean;
|
|
435
|
+
}
|
|
436
|
+
/** Parsed bundle shape the verifier consumes. Fields beyond these are ignored. */
|
|
437
|
+
interface AuditBundle {
|
|
438
|
+
export_id?: unknown;
|
|
439
|
+
org_id?: unknown;
|
|
440
|
+
chain_head_hash?: unknown;
|
|
441
|
+
event_count?: unknown;
|
|
442
|
+
signed_at?: unknown;
|
|
443
|
+
events?: unknown;
|
|
444
|
+
signature?: unknown;
|
|
445
|
+
signing_key_id?: unknown;
|
|
446
|
+
[k: string]: unknown;
|
|
447
|
+
}
|
|
448
|
+
interface VerifyBundleOptions {
|
|
449
|
+
/** SPKI-PEM strings (one per key in the active trust set). */
|
|
450
|
+
publicKeysPem?: readonly string[];
|
|
451
|
+
/** Already-imported keys, paired with registry ids (rotation hint). */
|
|
452
|
+
keys?: readonly VerifyKey[];
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Reproduces `_shared/rules.ts::canonicalJSON` byte-for-byte:
|
|
456
|
+
* - object keys sorted at every depth
|
|
457
|
+
* - no whitespace
|
|
458
|
+
* - `null`, `undefined`, `NaN`, `±Infinity` all render as `"null"`
|
|
459
|
+
* - strings use standard `JSON.stringify` escapes
|
|
460
|
+
*/
|
|
461
|
+
declare function canonicalJSON(value: unknown): string;
|
|
462
|
+
/**
|
|
463
|
+
* Recreate the exact bytes `handleExport` signed. Key order is
|
|
464
|
+
* load-bearing — must match the object literal in
|
|
465
|
+
* `v1-audit/index.ts::handleExport`. V8 preserves insertion order, so
|
|
466
|
+
* the literal below is byte-identical with what the backend signs.
|
|
467
|
+
*/
|
|
468
|
+
declare function signedBytesFor(bundle: AuditBundle): Uint8Array<ArrayBuffer>;
|
|
469
|
+
declare function verifyAuditBundle(bundle: AuditBundle, keys: readonly VerifyKey[]): Promise<BundleVerificationResult>;
|
|
470
|
+
/**
|
|
471
|
+
* Load a bundle from disk (or a parsed object) and verify it.
|
|
472
|
+
*
|
|
473
|
+
* `publicKeysPem` is the active SPKI-PEM set from
|
|
474
|
+
* `GET /v1-signing-keys`. When omitted, the chain check still runs
|
|
475
|
+
* but `signatureValid` will be false with an explanatory `reason` —
|
|
476
|
+
* callers that want a complete offline check MUST supply the trust
|
|
477
|
+
* set.
|
|
478
|
+
*/
|
|
479
|
+
declare function verifyBundle(pathOrBundle: string | AuditBundle, options?: VerifyBundleOptions): Promise<BundleVerificationResult>;
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Retry-policy helpers for the AtlaSent TypeScript SDK.
|
|
483
|
+
*
|
|
484
|
+
* This module is **pure**: no I/O, no network, no globals beyond
|
|
485
|
+
* `Math.random`. The intent is that {@link AtlaSentClient} (and any
|
|
486
|
+
* caller wrapping `protect()` / `evaluate()`) can ask
|
|
487
|
+
* {@link isRetryable} whether to retry a given {@link AtlaSentError},
|
|
488
|
+
* then ask {@link computeBackoffMs} how long to sleep before the next
|
|
489
|
+
* attempt.
|
|
490
|
+
*
|
|
491
|
+
* Wire-up into the client itself is intentionally deferred — see
|
|
492
|
+
* ROADMAP item #7 (Post-GA). Sentry breadcrumb emission is also
|
|
493
|
+
* deferred; both will land together once a transport-level retry
|
|
494
|
+
* loop is wired into `client.ts`.
|
|
495
|
+
*
|
|
496
|
+
* Retry classification (matches the server's documented contract):
|
|
497
|
+
* - `network` / `timeout` → retry (transient transport)
|
|
498
|
+
* - `server_error` (HTTP 5xx) → retry
|
|
499
|
+
* - `rate_limited` (HTTP 429) → retry, honour `retryAfterMs`
|
|
500
|
+
* - `bad_response` → retry (likely truncated body)
|
|
501
|
+
* - `invalid_api_key`/`forbidden`/`bad_request` → never retry
|
|
502
|
+
*
|
|
503
|
+
* Backoff: capped exponential with full jitter.
|
|
504
|
+
* delay = min(maxDelayMs, baseDelayMs * 2^attempt) * random[0, 1)
|
|
505
|
+
*
|
|
506
|
+
* "Full jitter" is the AWS-recommended scheme — see
|
|
507
|
+
* https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/.
|
|
508
|
+
* It avoids thundering-herd retries from many SDK instances that hit
|
|
509
|
+
* a 429 in the same window.
|
|
510
|
+
*/
|
|
511
|
+
/** Defaults for {@link RetryPolicy}. Conservative — three retries, ~7s ceiling. */
|
|
512
|
+
declare const DEFAULT_RETRY_POLICY: Required<RetryPolicy>;
|
|
513
|
+
/**
|
|
514
|
+
* Caller-tunable retry policy. All fields optional; missing fields
|
|
515
|
+
* fall back to {@link DEFAULT_RETRY_POLICY}.
|
|
516
|
+
*/
|
|
517
|
+
interface RetryPolicy {
|
|
518
|
+
/**
|
|
519
|
+
* Total attempts including the first try. `1` disables retries
|
|
520
|
+
* entirely. Must be `>= 1`; values below are clamped to `1`.
|
|
521
|
+
*/
|
|
522
|
+
maxAttempts?: number;
|
|
523
|
+
/**
|
|
524
|
+
* Initial backoff for `attempt = 0`. Doubles per attempt up to
|
|
525
|
+
* `maxDelayMs`. Must be `>= 0`.
|
|
526
|
+
*/
|
|
527
|
+
baseDelayMs?: number;
|
|
528
|
+
/**
|
|
529
|
+
* Hard ceiling on the per-attempt sleep, applied **before** jitter.
|
|
530
|
+
* The actual sleep is uniformly distributed in `[0, ceiling]`.
|
|
531
|
+
*/
|
|
532
|
+
maxDelayMs?: number;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Decide whether `err` is worth a retry. Anything that isn't an
|
|
536
|
+
* {@link AtlaSentError} is treated as non-retryable — the SDK's
|
|
537
|
+
* transport layer always wraps fetch failures in `AtlaSentError`,
|
|
538
|
+
* so a non-AtlaSent throwable is by definition a programmer bug
|
|
539
|
+
* (a bad input, an assertion in user code) and should propagate.
|
|
540
|
+
*/
|
|
541
|
+
declare function isRetryable(err: unknown): boolean;
|
|
542
|
+
/**
|
|
543
|
+
* Compute how long to sleep before retry attempt `attempt`
|
|
544
|
+
* (zero-indexed: `attempt = 0` is the first retry, i.e. the second
|
|
545
|
+
* total request). Uses capped exponential backoff with full jitter.
|
|
546
|
+
*
|
|
547
|
+
* When `err` carries a `retryAfterMs` (server-provided `Retry-After`
|
|
548
|
+
* header), the result is `max(retryAfterMs, jitteredDelay)` — the
|
|
549
|
+
* server's hint is treated as a floor so we never retry sooner than
|
|
550
|
+
* the server asked.
|
|
551
|
+
*
|
|
552
|
+
* @param attempt Zero-indexed retry attempt (0, 1, 2, ...).
|
|
553
|
+
* @param policy Optional override of {@link DEFAULT_RETRY_POLICY}.
|
|
554
|
+
* @param err Optional error whose `retryAfterMs` is honoured.
|
|
555
|
+
* @param random Injectable RNG, defaults to `Math.random`. Must
|
|
556
|
+
* return values in `[0, 1)` to preserve the
|
|
557
|
+
* distribution.
|
|
558
|
+
*/
|
|
559
|
+
declare function computeBackoffMs(attempt: number, policy?: RetryPolicy, err?: unknown, random?: () => number): number;
|
|
560
|
+
/**
|
|
561
|
+
* Returns `true` when `attempt` (zero-indexed) is below the policy's
|
|
562
|
+
* `maxAttempts - 1` ceiling — i.e. when there is still budget for at
|
|
563
|
+
* least one more try after this one. Convenience wrapper so retry
|
|
564
|
+
* loops read top-to-bottom:
|
|
565
|
+
*
|
|
566
|
+
* ```ts
|
|
567
|
+
* for (let attempt = 0; ; attempt++) {
|
|
568
|
+
* try { return await op(); }
|
|
569
|
+
* catch (err) {
|
|
570
|
+
* if (!isRetryable(err) || !hasAttemptsLeft(attempt, policy)) throw err;
|
|
571
|
+
* await sleep(computeBackoffMs(attempt, policy, err));
|
|
572
|
+
* }
|
|
573
|
+
* }
|
|
574
|
+
* ```
|
|
575
|
+
*/
|
|
576
|
+
declare function hasAttemptsLeft(attempt: number, policy?: RetryPolicy): boolean;
|
|
577
|
+
/**
|
|
578
|
+
* Merge a partial policy with {@link DEFAULT_RETRY_POLICY} and clamp
|
|
579
|
+
* each field into a sensible range. Exported for tests and for
|
|
580
|
+
* callers that want to log the resolved policy.
|
|
581
|
+
*/
|
|
582
|
+
declare function mergePolicy(policy: RetryPolicy): Required<RetryPolicy>;
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* @atlasent/sdk — execution-time authorization for AI agents.
|
|
586
|
+
*
|
|
587
|
+
* Primary API is the default export:
|
|
588
|
+
*
|
|
589
|
+
* ```ts
|
|
590
|
+
* import atlasent from "@atlasent/sdk";
|
|
591
|
+
*
|
|
592
|
+
* const permit = await atlasent.protect({
|
|
593
|
+
* agent: "deploy-bot",
|
|
594
|
+
* action: "deploy_to_production",
|
|
595
|
+
* context: { commit, approver },
|
|
596
|
+
* });
|
|
597
|
+
* ```
|
|
598
|
+
*
|
|
599
|
+
* Named exports remain available for the lower-level
|
|
600
|
+
* {@link AtlaSentClient} and the error taxonomy.
|
|
601
|
+
*/
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Default export. The opinionated, category-defining entry point:
|
|
605
|
+
*
|
|
606
|
+
* ```ts
|
|
607
|
+
* import atlasent from "@atlasent/sdk";
|
|
608
|
+
* await atlasent.protect({ ... });
|
|
609
|
+
* ```
|
|
610
|
+
*/
|
|
611
|
+
declare const atlasent: {
|
|
612
|
+
readonly protect: typeof protect;
|
|
613
|
+
readonly configure: typeof configure;
|
|
614
|
+
readonly verifyBundle: typeof verifyBundle;
|
|
615
|
+
readonly AtlaSentClient: typeof AtlaSentClient;
|
|
616
|
+
readonly AtlaSentError: typeof AtlaSentError;
|
|
617
|
+
readonly AtlaSentDeniedError: typeof AtlaSentDeniedError;
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
export { type ApiKeySelfResponse, AtlaSentClient, type AtlaSentClientOptions, AtlaSentDeniedError, AtlaSentError, type AuditBundle, type AuditDecision, type AuditEvent, type AuditEventsPage, type AuditEventsQuery, type AuditEventsResult, type AuditExport, type AuditExportRequest, type AuditExportResult, type AuditExportSignatureStatus, type BundleVerificationResult, DEFAULT_RETRY_POLICY, type Decision, type EvaluateRequest, type EvaluateResponse, type RateLimitState, type RetryPolicy, type VerifyBundleOptions, type VerifyKey, type VerifyPermitRequest, type VerifyPermitResponse, canonicalJSON, computeBackoffMs, configure, atlasent as default, hasAttemptsLeft, isRetryable, mergePolicy, protect, signedBytesFor, verifyAuditBundle, verifyBundle };
|