@atlasent/sdk 1.5.0 → 2.10.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 +141 -27
- package/dist/hono.cjs +1943 -64
- package/dist/hono.cjs.map +1 -1
- package/dist/hono.d.cts +4 -4
- package/dist/hono.d.ts +4 -4
- package/dist/hono.js +1933 -64
- package/dist/hono.js.map +1 -1
- package/dist/index.cjs +5299 -210
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5465 -502
- package/dist/index.d.ts +5465 -502
- package/dist/index.js +5177 -209
- package/dist/index.js.map +1 -1
- package/dist/protect-C0t0fP1y.d.cts +1776 -0
- package/dist/protect-C0t0fP1y.d.ts +1776 -0
- package/dist/state.cjs +46 -0
- package/dist/state.cjs.map +1 -0
- package/dist/state.d.cts +152 -0
- package/dist/state.d.ts +152 -0
- package/dist/state.js +21 -0
- package/dist/state.js.map +1 -0
- package/package.json +29 -2
- package/dist/protect-BKxcoR_2.d.cts +0 -159
- package/dist/protect-BKxcoR_2.d.ts +0 -159
|
@@ -0,0 +1,1776 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error types for the AtlaSent TypeScript SDK.
|
|
3
|
+
*
|
|
4
|
+
* The SDK follows a fail-closed design: a clean policy DENY is
|
|
5
|
+
* returned as `EvaluateResponse.decision === "deny"` (not thrown),
|
|
6
|
+
* but any failure to confirm authorization — network, timeout,
|
|
7
|
+
* bad response, invalid key, rate limit — throws an
|
|
8
|
+
* {@link AtlaSentError}.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Thrown when no SSE event arrives within the configured timeout window.
|
|
12
|
+
*
|
|
13
|
+
* Callers can catch this specifically to distinguish a stalled stream
|
|
14
|
+
* from other network or parse failures:
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* catch (e) {
|
|
18
|
+
* if (e instanceof StreamTimeoutError) { // reconnect or alert }
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare class StreamTimeoutError extends Error {
|
|
23
|
+
name: string;
|
|
24
|
+
/** Timeout that was exceeded, in milliseconds. */
|
|
25
|
+
readonly timeoutMs: number;
|
|
26
|
+
constructor(timeoutMs: number);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Thrown when the SSE stream closes with a partial / malformed JSON payload.
|
|
30
|
+
*
|
|
31
|
+
* This is a recoverable condition — the stream closed mid-JSON. The
|
|
32
|
+
* caller can reconnect using the last received `Last-Event-ID` and
|
|
33
|
+
* resume from where the server left off.
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* catch (e) {
|
|
37
|
+
* if (e instanceof StreamParseError) { // log raw data, maybe reconnect }
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
declare class StreamParseError extends Error {
|
|
42
|
+
name: string;
|
|
43
|
+
/** The raw data string that failed to parse. */
|
|
44
|
+
readonly rawData: string;
|
|
45
|
+
constructor(rawData: string, cause?: unknown);
|
|
46
|
+
}
|
|
47
|
+
/** Discriminator for {@link AtlaSentError.code}. */
|
|
48
|
+
type AtlaSentErrorCode = "invalid_api_key" | "forbidden" | "rate_limited" | "timeout" | "network" | "bad_response" | "bad_request" | "server_error" | "feature_disabled" | "claim_evidence_incomplete";
|
|
49
|
+
/** Initialization options for {@link AtlaSentError}. */
|
|
50
|
+
interface AtlaSentErrorInit {
|
|
51
|
+
status?: number;
|
|
52
|
+
code?: AtlaSentErrorCode;
|
|
53
|
+
requestId?: string;
|
|
54
|
+
retryAfterMs?: number;
|
|
55
|
+
cause?: unknown;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The only error type this SDK throws.
|
|
59
|
+
*
|
|
60
|
+
* Flat top-level properties mirror the convention used by Stripe,
|
|
61
|
+
* Octokit, and Supabase. `cause` is forwarded to the standard
|
|
62
|
+
* ES2022 `Error` constructor.
|
|
63
|
+
*/
|
|
64
|
+
declare class AtlaSentError extends Error {
|
|
65
|
+
name: string;
|
|
66
|
+
/** HTTP status code, when the error originated from an API response. */
|
|
67
|
+
readonly status: number | undefined;
|
|
68
|
+
/** Coarse category — useful for `switch` statements at call sites. */
|
|
69
|
+
readonly code: AtlaSentErrorCode | undefined;
|
|
70
|
+
/** Correlation ID echoed from the `X-Request-ID` header the SDK sent. */
|
|
71
|
+
readonly requestId: string | undefined;
|
|
72
|
+
/** Parsed `Retry-After` header value, in milliseconds. Only set for 429. */
|
|
73
|
+
readonly retryAfterMs: number | undefined;
|
|
74
|
+
constructor(message: string, init?: AtlaSentErrorInit);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Outcome of a denied decision.
|
|
78
|
+
*
|
|
79
|
+
* `"deny"` is what the current `/v1-evaluate` API returns. `"hold"`
|
|
80
|
+
* and `"escalate"` are reserved for forthcoming API decisions that
|
|
81
|
+
* put a permit into a pending state requiring human review; the
|
|
82
|
+
* union is declared now so call sites can `switch` exhaustively
|
|
83
|
+
* from the start and adopt new decisions without a breaking change.
|
|
84
|
+
*/
|
|
85
|
+
type AtlaSentDecision = "deny" | "hold" | "escalate";
|
|
86
|
+
/**
|
|
87
|
+
* Reason an already-issued permit failed verification.
|
|
88
|
+
*
|
|
89
|
+
* Surfaced on {@link AtlaSentDeniedError.outcome} so callers can
|
|
90
|
+
* distinguish replay (`permit_consumed`) from revocation
|
|
91
|
+
* (`permit_revoked`) from natural expiry (`permit_expired`) without
|
|
92
|
+
* parsing {@link AtlaSentDeniedError.reason}. The set is defined by
|
|
93
|
+
* `contract/vectors/permit_outcomes.json`; any new outcome MUST be
|
|
94
|
+
* added there first.
|
|
95
|
+
*
|
|
96
|
+
* Mirrors the Python SDK's `PermitOutcome`. See
|
|
97
|
+
* `atlasent/docs/REVOCATION_RUNBOOK.md` for the operator-facing
|
|
98
|
+
* matrix this discriminator drives.
|
|
99
|
+
*/
|
|
100
|
+
type PermitOutcome = "permit_consumed" | "permit_expired" | "permit_revoked" | "permit_not_found";
|
|
101
|
+
/**
|
|
102
|
+
* Map a server-supplied `outcome` string to {@link PermitOutcome}.
|
|
103
|
+
*
|
|
104
|
+
* Returns `undefined` for `undefined`, `""`, `"verified"`, or any
|
|
105
|
+
* unrecognized value. Used at the SDK's deny boundary so we don't
|
|
106
|
+
* surface mis-typed outcomes — when the server adds a new outcome
|
|
107
|
+
* string, callers branching on {@link AtlaSentDeniedError.outcome}
|
|
108
|
+
* see `undefined` and fall through to their generic deny path
|
|
109
|
+
* rather than match an unknown literal.
|
|
110
|
+
*/
|
|
111
|
+
declare function normalizePermitOutcome(raw: string | undefined): PermitOutcome | undefined;
|
|
112
|
+
/** Initialization options for {@link AtlaSentDeniedError}. */
|
|
113
|
+
interface AtlaSentDeniedErrorInit {
|
|
114
|
+
decision: AtlaSentDecision;
|
|
115
|
+
evaluationId: string;
|
|
116
|
+
reason?: string;
|
|
117
|
+
requestId?: string;
|
|
118
|
+
auditHash?: string;
|
|
119
|
+
/**
|
|
120
|
+
* When the denial came from permit verification (not policy
|
|
121
|
+
* evaluation), the discriminator that distinguishes replay,
|
|
122
|
+
* expiry, revocation, and missing-record failures. `undefined`
|
|
123
|
+
* for evaluate-time denials.
|
|
124
|
+
*/
|
|
125
|
+
outcome?: PermitOutcome;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Thrown by {@link atlasent.protect} when the policy engine refuses
|
|
129
|
+
* the action, or when a permit fails end-to-end verification.
|
|
130
|
+
*
|
|
131
|
+
* This is the **fail-closed boundary** of the SDK: every code path
|
|
132
|
+
* that short-circuits an action because authorization was not
|
|
133
|
+
* confirmed raises an `AtlaSentDeniedError`. Callers cannot silently
|
|
134
|
+
* proceed on a denial by forgetting to branch on a return value.
|
|
135
|
+
*
|
|
136
|
+
* Extends {@link AtlaSentError} so `instanceof AtlaSentError`
|
|
137
|
+
* catches denials as part of the SDK's single exception family;
|
|
138
|
+
* use `instanceof AtlaSentDeniedError` to distinguish a policy
|
|
139
|
+
* denial from a transport/auth error.
|
|
140
|
+
*/
|
|
141
|
+
declare class AtlaSentDeniedError extends AtlaSentError {
|
|
142
|
+
name: string;
|
|
143
|
+
/** Policy decision — `"deny"` today; `"hold"` / `"escalate"` reserved. */
|
|
144
|
+
readonly decision: AtlaSentDecision;
|
|
145
|
+
/** Opaque permit/decision id from `/v1-evaluate`. */
|
|
146
|
+
readonly evaluationId: string;
|
|
147
|
+
/** Human-readable explanation from the policy engine, if provided. */
|
|
148
|
+
readonly reason: string | undefined;
|
|
149
|
+
/** Hash-chained audit-trail entry associated with the decision. */
|
|
150
|
+
readonly auditHash: string | undefined;
|
|
151
|
+
/**
|
|
152
|
+
* Discriminator for permit-side denial reasons. Populated only
|
|
153
|
+
* when the server reported `verified=false` from `/v1-verify-permit`;
|
|
154
|
+
* `undefined` for evaluate-time denials. See {@link PermitOutcome}.
|
|
155
|
+
*/
|
|
156
|
+
readonly outcome: PermitOutcome | undefined;
|
|
157
|
+
constructor(init: AtlaSentDeniedErrorInit);
|
|
158
|
+
/** `true` when the permit was explicitly revoked (D3 endpoint). */
|
|
159
|
+
get isRevoked(): boolean;
|
|
160
|
+
/** `true` when the permit's TTL passed before verification. */
|
|
161
|
+
get isExpired(): boolean;
|
|
162
|
+
/**
|
|
163
|
+
* `true` when the permit was already consumed by a prior verify
|
|
164
|
+
* (v1 single-use replay protection).
|
|
165
|
+
*/
|
|
166
|
+
get isConsumed(): boolean;
|
|
167
|
+
/**
|
|
168
|
+
* `true` when the permit id wasn't recognized server-side
|
|
169
|
+
* (typo, cross-tenant lookup, or pre-issuance race).
|
|
170
|
+
*/
|
|
171
|
+
get isNotFound(): boolean;
|
|
172
|
+
}
|
|
173
|
+
/** Initialization options for {@link AtlaSentEscalateError}. */
|
|
174
|
+
interface AtlaSentEscalateErrorInit {
|
|
175
|
+
requestId?: string;
|
|
176
|
+
userId?: string;
|
|
177
|
+
cause?: unknown;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Thrown when an evaluate response carries `decision: "escalate"`.
|
|
181
|
+
*
|
|
182
|
+
* Distinct from {@link AtlaSentDeniedError} — an escalation does not
|
|
183
|
+
* constitute a hard denial. It signals that the policy engine has
|
|
184
|
+
* deferred the authorization decision to a human review queue.
|
|
185
|
+
* Middleware and agent orchestrators should catch this specifically
|
|
186
|
+
* and route the pending action to the appropriate HITL channel.
|
|
187
|
+
*
|
|
188
|
+
* ```ts
|
|
189
|
+
* catch (e) {
|
|
190
|
+
* if (e instanceof AtlaSentEscalateError) {
|
|
191
|
+
* await humanReviewQueue.submit({ userId: e.userId, requestId: e.requestId });
|
|
192
|
+
* }
|
|
193
|
+
* }
|
|
194
|
+
* ```
|
|
195
|
+
*
|
|
196
|
+
* Extends {@link AtlaSentError} so `instanceof AtlaSentError` catches
|
|
197
|
+
* escalations alongside other SDK errors; use
|
|
198
|
+
* `instanceof AtlaSentEscalateError` to branch specifically.
|
|
199
|
+
*/
|
|
200
|
+
declare class AtlaSentEscalateError extends AtlaSentError {
|
|
201
|
+
name: string;
|
|
202
|
+
/** Always `"escalate"` — discriminates this error from other AtlaSent errors. */
|
|
203
|
+
readonly decision: "escalate";
|
|
204
|
+
/** The user whose action triggered the escalation, if available. */
|
|
205
|
+
readonly userId: string | undefined;
|
|
206
|
+
constructor(message: string, opts?: AtlaSentEscalateErrorInit);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Thrown by an SDK guard heartbeat when `GET /v1/permits/:id/valid`
|
|
210
|
+
* returns `status: 'revoked'` during tool execution (PROD-D9
|
|
211
|
+
* continuous-authorization lease model).
|
|
212
|
+
*
|
|
213
|
+
* This error is **always re-thrown** — it is never serialized as a
|
|
214
|
+
* `tool-result` denial because it represents a live enforcement action,
|
|
215
|
+
* not a policy evaluation at request time. Callers should treat it as
|
|
216
|
+
* an immediate halt signal.
|
|
217
|
+
*
|
|
218
|
+
* ```ts
|
|
219
|
+
* catch (e) {
|
|
220
|
+
* if (e instanceof PermitRevoked) {
|
|
221
|
+
* // log e.permitId and e.revocationId for incident correlation
|
|
222
|
+
* await incidentLog.record({ permitId: e.permitId, revocationId: e.revocationId });
|
|
223
|
+
* }
|
|
224
|
+
* }
|
|
225
|
+
* ```
|
|
226
|
+
*
|
|
227
|
+
* Guard heartbeat is configured via `permitRevalidationIntervalMs` in
|
|
228
|
+
* the guard options (minimum 1000 ms). The heartbeat activates only
|
|
229
|
+
* when the {@link AtlaSentClient} exposes `checkPermitValid` — i.e.
|
|
230
|
+
* when `atlasent-api` has deployed `GET /v1/permits/:id/valid`.
|
|
231
|
+
*/
|
|
232
|
+
declare class PermitRevoked extends AtlaSentError {
|
|
233
|
+
name: string;
|
|
234
|
+
/** The id of the permit that was revoked mid-execution. */
|
|
235
|
+
readonly permitId: string;
|
|
236
|
+
/** The `scope_revocations.id` that triggered the revocation, when available. */
|
|
237
|
+
readonly revocationId: string | undefined;
|
|
238
|
+
constructor(permitId: string, revocationId?: string);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Retry-policy helpers for the AtlaSent TypeScript SDK.
|
|
243
|
+
*
|
|
244
|
+
* This module is **pure**: no I/O, no network, no globals beyond
|
|
245
|
+
* `Math.random`. The intent is that {@link AtlaSentClient} (and any
|
|
246
|
+
* caller wrapping `protect()` / `evaluate()`) can ask
|
|
247
|
+
* {@link isRetryable} whether to retry a given {@link AtlaSentError},
|
|
248
|
+
* then ask {@link computeBackoffMs} how long to sleep before the next
|
|
249
|
+
* attempt.
|
|
250
|
+
*
|
|
251
|
+
* Wire-up into the client itself is intentionally deferred — see
|
|
252
|
+
* ROADMAP item #7 (Post-GA). Sentry breadcrumb emission is also
|
|
253
|
+
* deferred; both will land together once a transport-level retry
|
|
254
|
+
* loop is wired into `client.ts`.
|
|
255
|
+
*
|
|
256
|
+
* Retry classification (matches the server's documented contract):
|
|
257
|
+
* - `network` / `timeout` → retry (transient transport)
|
|
258
|
+
* - `server_error` (HTTP 5xx) → retry
|
|
259
|
+
* - `rate_limited` (HTTP 429) → retry, honour `retryAfterMs`
|
|
260
|
+
* - `bad_response` → retry (likely truncated body)
|
|
261
|
+
* - `invalid_api_key`/`forbidden`/`bad_request` → never retry
|
|
262
|
+
*
|
|
263
|
+
* Backoff: capped exponential with full jitter.
|
|
264
|
+
* delay = min(maxDelayMs, baseDelayMs * 2^attempt) * random[0, 1)
|
|
265
|
+
*
|
|
266
|
+
* "Full jitter" is the AWS-recommended scheme — see
|
|
267
|
+
* https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/.
|
|
268
|
+
* It avoids thundering-herd retries from many SDK instances that hit
|
|
269
|
+
* a 429 in the same window.
|
|
270
|
+
*
|
|
271
|
+
* Default schedule matches the Python SDK:
|
|
272
|
+
* attempt 0 (1st retry): base 2 000 ms, jittered in [0, 2 000)
|
|
273
|
+
* attempt 1 (2nd retry): base 4 000 ms, jittered in [0, 4 000)
|
|
274
|
+
* attempt 2 (3rd retry): base 8 000 ms, jittered in [0, 8 000)
|
|
275
|
+
* attempt 3 (4th retry): capped at 16 000 ms, jittered in [0, 16 000)
|
|
276
|
+
* Total attempts (including initial): 4
|
|
277
|
+
*/
|
|
278
|
+
/**
|
|
279
|
+
* Defaults for {@link RetryPolicy}.
|
|
280
|
+
*
|
|
281
|
+
* Matches the Python SDK's backoff schedule:
|
|
282
|
+
* 2 s → 4 s → 8 s → 16 s (4 total attempts, cap at 16 s).
|
|
283
|
+
*/
|
|
284
|
+
declare const DEFAULT_RETRY_POLICY: Required<RetryPolicy>;
|
|
285
|
+
/**
|
|
286
|
+
* Caller-tunable retry policy. All fields optional; missing fields
|
|
287
|
+
* fall back to {@link DEFAULT_RETRY_POLICY}.
|
|
288
|
+
*/
|
|
289
|
+
interface RetryPolicy {
|
|
290
|
+
/**
|
|
291
|
+
* Total attempts including the first try. `1` disables retries
|
|
292
|
+
* entirely. Must be `>= 1`; values below are clamped to `1`.
|
|
293
|
+
*/
|
|
294
|
+
maxAttempts?: number;
|
|
295
|
+
/**
|
|
296
|
+
* Initial backoff for `attempt = 0`. Doubles per attempt up to
|
|
297
|
+
* `maxDelayMs`. Must be `>= 0`.
|
|
298
|
+
*/
|
|
299
|
+
baseDelayMs?: number;
|
|
300
|
+
/**
|
|
301
|
+
* Hard ceiling on the per-attempt sleep, applied **before** jitter.
|
|
302
|
+
* The actual sleep is uniformly distributed in `[0, ceiling]`.
|
|
303
|
+
*/
|
|
304
|
+
maxDelayMs?: number;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Decide whether `err` is worth a retry. Anything that isn't an
|
|
308
|
+
* {@link AtlaSentError} is treated as non-retryable — the SDK's
|
|
309
|
+
* transport layer always wraps fetch failures in `AtlaSentError`,
|
|
310
|
+
* so a non-AtlaSent throwable is by definition a programmer bug
|
|
311
|
+
* (a bad input, an assertion in user code) and should propagate.
|
|
312
|
+
*/
|
|
313
|
+
declare function isRetryable(err: unknown): boolean;
|
|
314
|
+
/**
|
|
315
|
+
* Compute how long to sleep before retry attempt `attempt`
|
|
316
|
+
* (zero-indexed: `attempt = 0` is the first retry, i.e. the second
|
|
317
|
+
* total request). Uses capped exponential backoff with full jitter.
|
|
318
|
+
*
|
|
319
|
+
* When `err` carries a `retryAfterMs` (server-provided `Retry-After`
|
|
320
|
+
* header), the result is `max(retryAfterMs, jitteredDelay)` — the
|
|
321
|
+
* server's hint is treated as a floor so we never retry sooner than
|
|
322
|
+
* the server asked.
|
|
323
|
+
*
|
|
324
|
+
* @param attempt Zero-indexed retry attempt (0, 1, 2, ...). For
|
|
325
|
+
* the default policy this produces delays drawn from
|
|
326
|
+
* [0, 2 000), [0, 4 000), [0, 8 000), [0, 16 000).
|
|
327
|
+
* @param policy Optional override of {@link DEFAULT_RETRY_POLICY}.
|
|
328
|
+
* @param err Optional error whose `retryAfterMs` is honoured.
|
|
329
|
+
* @param random Injectable RNG, defaults to `Math.random`. Must
|
|
330
|
+
* return values in `[0, 1)` to preserve the
|
|
331
|
+
* distribution.
|
|
332
|
+
*/
|
|
333
|
+
declare function computeBackoffMs(attempt: number, policy?: RetryPolicy, err?: unknown, random?: () => number): number;
|
|
334
|
+
/**
|
|
335
|
+
* Returns `true` when `attempt` (zero-indexed) is below the policy's
|
|
336
|
+
* `maxAttempts - 1` ceiling — i.e. when there is still budget for at
|
|
337
|
+
* least one more try after this one. Convenience wrapper so retry
|
|
338
|
+
* loops read top-to-bottom:
|
|
339
|
+
*
|
|
340
|
+
* ```ts
|
|
341
|
+
* for (let attempt = 0; ; attempt++) {
|
|
342
|
+
* try { return await op(); }
|
|
343
|
+
* catch (err) {
|
|
344
|
+
* if (!isRetryable(err) || !hasAttemptsLeft(attempt, policy)) throw err;
|
|
345
|
+
* await sleep(computeBackoffMs(attempt, policy, err));
|
|
346
|
+
* }
|
|
347
|
+
* }
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
declare function hasAttemptsLeft(attempt: number, policy?: RetryPolicy): boolean;
|
|
351
|
+
/**
|
|
352
|
+
* Merge a partial policy with {@link DEFAULT_RETRY_POLICY} and clamp
|
|
353
|
+
* each field into a sensible range. Exported for tests and for
|
|
354
|
+
* callers that want to log the resolved policy.
|
|
355
|
+
*/
|
|
356
|
+
declare function mergePolicy(policy: RetryPolicy): Required<RetryPolicy>;
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Shared audit wire types — the `/v1/audit/*` HTTP surface.
|
|
360
|
+
*
|
|
361
|
+
* Source of truth: `atlasent-api/supabase/functions/v1-audit/index.ts`
|
|
362
|
+
* (the edge function that serves `GET /v1/audit/events`,
|
|
363
|
+
* `POST /v1/audit/exports`, and `POST /v1/audit/verify`). The docstring
|
|
364
|
+
* at the top of that file describes these shapes. Keep this module in
|
|
365
|
+
* lockstep with it; `test/audit-types.test.ts` contains type-level
|
|
366
|
+
* assertions against the field set to make drift obvious.
|
|
367
|
+
*
|
|
368
|
+
* Fields are snake_case because that is what the server emits on the
|
|
369
|
+
* wire — unlike the evaluate / verify-permit types in `./types.ts`,
|
|
370
|
+
* which use camelCase on the SDK side and translate at the client
|
|
371
|
+
* boundary, the audit surface is intentionally wire-identical so that
|
|
372
|
+
* signed export bundles round-trip byte-for-byte through verifiers.
|
|
373
|
+
*/
|
|
374
|
+
/** Policy decision enum used on `audit_events.decision`. */
|
|
375
|
+
type AuditDecision = 'allow' | 'deny' | 'hold' | 'escalate';
|
|
376
|
+
/**
|
|
377
|
+
* Signing status reported on an export bundle. "signed" is the normal
|
|
378
|
+
* path; "unsigned" means the deployment has no active signing key;
|
|
379
|
+
* "signing_failed" means a key is configured but signing errored and
|
|
380
|
+
* the export was returned anyway (the signature is an empty string).
|
|
381
|
+
*/
|
|
382
|
+
type AuditExportSignatureStatus = 'signed' | 'unsigned' | 'signing_failed';
|
|
383
|
+
/**
|
|
384
|
+
* One persisted row from `audit_events`, as returned by
|
|
385
|
+
* `GET /v1/audit/events` and embedded inside `AuditExport.events`.
|
|
386
|
+
*
|
|
387
|
+
* `decision` is nullable because not every event is an evaluation —
|
|
388
|
+
* CRUD-style audit writes (e.g. `policy.updated`) omit it. All other
|
|
389
|
+
* nullable fields follow the same "field doesn't apply to this event
|
|
390
|
+
* type" convention rather than "unknown value".
|
|
391
|
+
*/
|
|
392
|
+
interface AuditEvent {
|
|
393
|
+
/** Event UUID. Stable; surfaces as `tampered_event_ids` on verify failure. */
|
|
394
|
+
id: string;
|
|
395
|
+
/** Organization this event belongs to. */
|
|
396
|
+
org_id: string;
|
|
397
|
+
/** Per-org monotonic sequence. Used as the pagination cursor's payload. */
|
|
398
|
+
sequence: number;
|
|
399
|
+
/** Event type tag (e.g. "evaluate.allow", "policy.updated"). */
|
|
400
|
+
type: string;
|
|
401
|
+
/** Policy decision when the event is an evaluation; `null` otherwise. */
|
|
402
|
+
decision: AuditDecision | null;
|
|
403
|
+
/** Actor id (user / API key / agent) when applicable. */
|
|
404
|
+
actor_id: string | null;
|
|
405
|
+
/** Optional resource tag — e.g. "policy". */
|
|
406
|
+
resource_type: string | null;
|
|
407
|
+
/** Optional resource id — paired with `resource_type`. */
|
|
408
|
+
resource_id: string | null;
|
|
409
|
+
/** Canonical JSON of the event payload. Hashed into `hash`. */
|
|
410
|
+
payload: Record<string, unknown> | null;
|
|
411
|
+
/** SHA-256(prev_hash || canonicalJSON(payload)), hex. */
|
|
412
|
+
hash: string;
|
|
413
|
+
/** Previous event's `hash` (genesis is `"0".repeat(64)`). */
|
|
414
|
+
previous_hash: string;
|
|
415
|
+
/** When the underlying action occurred (ISO 8601). */
|
|
416
|
+
occurred_at: string;
|
|
417
|
+
/** When the row was persisted (ISO 8601). */
|
|
418
|
+
created_at: string;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Response shape for `GET /v1/audit/events`.
|
|
422
|
+
*
|
|
423
|
+
* `total` is the filter's full count (not just this page) so callers
|
|
424
|
+
* can show "page 1 of N" without an extra HEAD request.
|
|
425
|
+
*
|
|
426
|
+
* `next_cursor` is an opaque base64url string. Pass it back verbatim
|
|
427
|
+
* as `?cursor=...` to fetch the next page. Absent when this is the
|
|
428
|
+
* last page.
|
|
429
|
+
*/
|
|
430
|
+
interface AuditEventsPage {
|
|
431
|
+
events: AuditEvent[];
|
|
432
|
+
total: number;
|
|
433
|
+
next_cursor?: string;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Query parameters accepted by `GET /v1/audit/events`. Serialize as
|
|
437
|
+
* URL search params — `types` is a comma-joined list on the wire
|
|
438
|
+
* (e.g. `types=evaluate.allow,policy.updated`).
|
|
439
|
+
*
|
|
440
|
+
* All fields are optional; the server defaults `limit` to 50 and caps
|
|
441
|
+
* it at 500.
|
|
442
|
+
*/
|
|
443
|
+
interface AuditEventsQuery {
|
|
444
|
+
/** Comma-joined list of event types to filter on. */
|
|
445
|
+
types?: string;
|
|
446
|
+
/** Filter to a single actor. */
|
|
447
|
+
actor_id?: string;
|
|
448
|
+
/** Inclusive lower bound on `occurred_at` (ISO 8601). */
|
|
449
|
+
from?: string;
|
|
450
|
+
/** Inclusive upper bound on `occurred_at` (ISO 8601). */
|
|
451
|
+
to?: string;
|
|
452
|
+
/** Page size. Default 50, min 1, max 500. */
|
|
453
|
+
limit?: number;
|
|
454
|
+
/** Opaque cursor returned as `next_cursor` by the prior page. */
|
|
455
|
+
cursor?: string;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Response shape for `POST /v1/audit/exports` — a signed bundle of
|
|
459
|
+
* audit events suitable for offline verification.
|
|
460
|
+
*
|
|
461
|
+
* `signature` is detached Ed25519 over `signedBytesFor(bundle)` (see
|
|
462
|
+
* `./auditBundle.ts`). An empty string means the server attempted to
|
|
463
|
+
* sign but failed; check `signature_status` to distinguish.
|
|
464
|
+
*
|
|
465
|
+
* `tampered_event_ids` surfaces rows whose recomputed hash doesn't
|
|
466
|
+
* match the stored hash — even when `chain_integrity_ok` is false,
|
|
467
|
+
* the signature still covers whatever the server emitted, so callers
|
|
468
|
+
* that trust the signature must still inspect this list.
|
|
469
|
+
*/
|
|
470
|
+
interface AuditExport {
|
|
471
|
+
/** Server-assigned UUID for this export. */
|
|
472
|
+
export_id: string;
|
|
473
|
+
/** Organization the bundle belongs to. */
|
|
474
|
+
org_id: string;
|
|
475
|
+
/** Events in canonical (ascending sequence) order. */
|
|
476
|
+
events: AuditEvent[];
|
|
477
|
+
/** Last event's `hash`, or `"0".repeat(64)` if `events` is empty. */
|
|
478
|
+
chain_head_hash: string;
|
|
479
|
+
/** `true` iff adjacency + re-hash succeeded for every event. */
|
|
480
|
+
chain_integrity_ok: boolean;
|
|
481
|
+
/** `AuditEvent.id`s whose recomputed hash != stored hash. */
|
|
482
|
+
tampered_event_ids: string[];
|
|
483
|
+
/** Detached Ed25519 signature (base64url). Empty string on sign failure. */
|
|
484
|
+
signature: string;
|
|
485
|
+
/** Outcome of the signing attempt. */
|
|
486
|
+
signature_status: AuditExportSignatureStatus;
|
|
487
|
+
/** Registry id of the key that signed — absent when unsigned. */
|
|
488
|
+
signing_key_id?: string;
|
|
489
|
+
/** When the bundle was signed (ISO 8601). */
|
|
490
|
+
signed_at: string;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Canonical 4-value policy decision, byte-identical to the wire.
|
|
495
|
+
*
|
|
496
|
+
* - `allow` — action is authorized; a Permit is issued.
|
|
497
|
+
* - `deny` — action is blocked.
|
|
498
|
+
* - `hold` — decision deferred (e.g. waiting on an approval signal).
|
|
499
|
+
* - `escalate` — routed to a human reviewer queue.
|
|
500
|
+
*
|
|
501
|
+
* Pin to this type on new code.
|
|
502
|
+
*/
|
|
503
|
+
type DecisionCanonical = "allow" | "deny" | "hold" | "escalate";
|
|
504
|
+
/**
|
|
505
|
+
* Decision type — unified with the canonical 4-value vocabulary.
|
|
506
|
+
*
|
|
507
|
+
* This type previously emitted `"ALLOW"` / `"DENY"` (uppercase, 2-value).
|
|
508
|
+
* It now reflects the canonical wire values (`"allow" | "deny" | "hold" |
|
|
509
|
+
* "escalate"`) so the `decision` and `decision_canonical` fields on
|
|
510
|
+
* {@link EvaluateResponse} carry identical values and types.
|
|
511
|
+
*
|
|
512
|
+
* Backward compatibility: the SDK normalises API response values to
|
|
513
|
+
* lowercase (`.toLowerCase()`) before returning them, so callers that
|
|
514
|
+
* previously checked `=== "ALLOW"` must update to `=== "allow"`. The
|
|
515
|
+
* canonical field `decision_canonical` is also available and was always
|
|
516
|
+
* lowercase — prefer it on new code.
|
|
517
|
+
*
|
|
518
|
+
* Legacy uppercase input accepted by the SDK is normalised to lowercase
|
|
519
|
+
* output; `"ALLOW"` in → `"allow"` out, `"DENY"` in → `"deny"` out.
|
|
520
|
+
*/
|
|
521
|
+
type Decision = DecisionCanonical;
|
|
522
|
+
/**
|
|
523
|
+
* Rate-limit state parsed from the server's `X-RateLimit-*` headers.
|
|
524
|
+
*
|
|
525
|
+
* Present on every authenticated response (success and 429) when the
|
|
526
|
+
* server emits the headers. `null` when the server doesn't — older
|
|
527
|
+
* deployments, or internal endpoints that skip per-key rate limiting.
|
|
528
|
+
*
|
|
529
|
+
* Clients should check `remaining` and sleep until `resetAt` to
|
|
530
|
+
* preemptively back off before hitting a 429.
|
|
531
|
+
*/
|
|
532
|
+
interface RateLimitState {
|
|
533
|
+
/** Value of `X-RateLimit-Limit` — the per-minute budget. */
|
|
534
|
+
limit: number;
|
|
535
|
+
/** Value of `X-RateLimit-Remaining` — unused budget in the current window. */
|
|
536
|
+
remaining: number;
|
|
537
|
+
/**
|
|
538
|
+
* Parsed `X-RateLimit-Reset` — the UTC instant when the current
|
|
539
|
+
* window's counter zeroes. Accepts either a unix-seconds integer or
|
|
540
|
+
* an ISO 8601 string on the wire.
|
|
541
|
+
*/
|
|
542
|
+
resetAt: Date;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Canonical Deploy Gate V1 protected action.
|
|
546
|
+
*
|
|
547
|
+
* Use this constant (or its string value `"production.deploy"`) on all
|
|
548
|
+
* new code. Server-side `action_classes.slug` was canonicalised to
|
|
549
|
+
* `production.deploy` in atlasent-api PR #662 / atlasent-console
|
|
550
|
+
* PR #432; the SDK default now matches.
|
|
551
|
+
*/
|
|
552
|
+
declare const PRODUCTION_DEPLOY_ACTION: "production.deploy";
|
|
553
|
+
/**
|
|
554
|
+
* Legacy alias for {@link PRODUCTION_DEPLOY_ACTION}.
|
|
555
|
+
*
|
|
556
|
+
* @deprecated since 2.3.0 — use {@link PRODUCTION_DEPLOY_ACTION}. The
|
|
557
|
+
* server alias-tolerates `deployment.production` during the V1 alias
|
|
558
|
+
* window, so existing callers continue to work unchanged; please
|
|
559
|
+
* migrate by the next minor release.
|
|
560
|
+
*/
|
|
561
|
+
declare const DEPLOYMENT_PRODUCTION_ACTION: "deployment.production";
|
|
562
|
+
/**
|
|
563
|
+
* Permit claim for `production.deploy` evaluations (Rule 3).
|
|
564
|
+
*
|
|
565
|
+
* Pass as `permit` inside {@link DeployGateContext}.
|
|
566
|
+
* The `verified` flag is set by the verify-permit service after a
|
|
567
|
+
* successful `/v1-verify-permit` call — do not self-assert it.
|
|
568
|
+
*/
|
|
569
|
+
interface DeployPermitClaim {
|
|
570
|
+
permit_id?: string;
|
|
571
|
+
environment?: string;
|
|
572
|
+
action_type?: string;
|
|
573
|
+
/** ISO-8601 timestamp when the permit was issued. */
|
|
574
|
+
issued_at?: string;
|
|
575
|
+
/** Set server-side by the verify-permit service. Do not self-assert. */
|
|
576
|
+
verified?: boolean;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Override claim for `production.deploy` evaluations (Rule 8).
|
|
580
|
+
*
|
|
581
|
+
* Both `override_reason` and `authority_basis` must be non-empty to
|
|
582
|
+
* receive `OVERRIDE_APPROVED`. Missing or blank fields return `DENY_POLICY`.
|
|
583
|
+
*/
|
|
584
|
+
interface DeployOverrideClaim {
|
|
585
|
+
/** Human-readable reason. Required and non-empty. */
|
|
586
|
+
override_reason?: string;
|
|
587
|
+
/** Authoritative basis — runbook section, incident ticket, etc. Required and non-empty. */
|
|
588
|
+
authority_basis?: string;
|
|
589
|
+
/** Approver actor ID (audit record; does not gate the decision). */
|
|
590
|
+
approver_actor_id?: string;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Typed context shape for `production.deploy` evaluations.
|
|
594
|
+
*
|
|
595
|
+
* Pass as `context` to `protect()`, `deployGate()`, or
|
|
596
|
+
* {@link AtlaSentClient.evaluate} for the Deploy Gate V1 flow.
|
|
597
|
+
*
|
|
598
|
+
* @example
|
|
599
|
+
* ```ts
|
|
600
|
+
* const permit = await atlasent.protect({
|
|
601
|
+
* agent: "deploy-bot",
|
|
602
|
+
* action: PRODUCTION_DEPLOY_ACTION,
|
|
603
|
+
* context: {
|
|
604
|
+
* environment: "production",
|
|
605
|
+
* evaluation_confirmed: true,
|
|
606
|
+
* actorMetadata: { role: "deploy_engineer" },
|
|
607
|
+
* permit: {
|
|
608
|
+
* permit_id: permitToken,
|
|
609
|
+
* environment: "production",
|
|
610
|
+
* action_type: PRODUCTION_DEPLOY_ACTION,
|
|
611
|
+
* issued_at: new Date().toISOString(),
|
|
612
|
+
* verified: true,
|
|
613
|
+
* },
|
|
614
|
+
* } satisfies DeployGateContext,
|
|
615
|
+
* });
|
|
616
|
+
* ```
|
|
617
|
+
*/
|
|
618
|
+
interface DeployGateContext {
|
|
619
|
+
/** Must be `"production"` for the production gate to apply. */
|
|
620
|
+
environment?: "production" | "staging" | "development";
|
|
621
|
+
/**
|
|
622
|
+
* When `true`, all rule failures are shadowed to `allow` (fail-open).
|
|
623
|
+
* Malformed-timestamp inconsistencies still escalate.
|
|
624
|
+
* Use for initial rollout before locking enforcement.
|
|
625
|
+
*/
|
|
626
|
+
pilot_mode?: boolean;
|
|
627
|
+
/** Must be `true` — confirms an evaluation record exists before proceeding. */
|
|
628
|
+
evaluation_confirmed?: boolean;
|
|
629
|
+
/** ISO-8601 timestamp of when evaluation was confirmed. */
|
|
630
|
+
evaluation_confirmed_at?: string;
|
|
631
|
+
/** Actor role metadata. `role` must be one of the approved deploy roles. */
|
|
632
|
+
actorMetadata?: {
|
|
633
|
+
role?: string;
|
|
634
|
+
};
|
|
635
|
+
/** Signed permit claim — required for non-pilot production deployments. */
|
|
636
|
+
permit?: DeployPermitClaim;
|
|
637
|
+
/** Override claim — short-circuits all rules when both fields are non-empty. */
|
|
638
|
+
override?: DeployOverrideClaim;
|
|
639
|
+
[key: string]: unknown;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Canonical deploy gate decision codes emitted for `production.deploy`.
|
|
643
|
+
*
|
|
644
|
+
* Appears as `deny_code` / `matchedRuleId` on evaluation responses.
|
|
645
|
+
* Pin dashboards, alerting, and routing logic to these codes — not to
|
|
646
|
+
* `deny_reason` strings, which may change.
|
|
647
|
+
*/
|
|
648
|
+
type DeployGateDenyCode = "ALLOW" | "DENY_POLICY" | "DENY_AUTHORITY" | "DENY_ENVIRONMENT" | "PERMIT_EXPIRED" | "VERIFY_FAILED" | "ESCALATE_REQUIRED" | "OVERRIDE_APPROVED";
|
|
649
|
+
/** Typed constants for {@link DeployGateDenyCode}. */
|
|
650
|
+
declare const DEPLOY_GATE_CODES: Readonly<{
|
|
651
|
+
ALLOW: "ALLOW";
|
|
652
|
+
DENY_POLICY: "DENY_POLICY";
|
|
653
|
+
DENY_AUTHORITY: "DENY_AUTHORITY";
|
|
654
|
+
DENY_ENVIRONMENT: "DENY_ENVIRONMENT";
|
|
655
|
+
PERMIT_EXPIRED: "PERMIT_EXPIRED";
|
|
656
|
+
VERIFY_FAILED: "VERIFY_FAILED";
|
|
657
|
+
ESCALATE_REQUIRED: "ESCALATE_REQUIRED";
|
|
658
|
+
OVERRIDE_APPROVED: "OVERRIDE_APPROVED";
|
|
659
|
+
}>;
|
|
660
|
+
/** Input to {@link AtlaSentClient.deployGate}. */
|
|
661
|
+
interface DeployGateRequest {
|
|
662
|
+
/** CI/repo actor performing the deployment. Defaults to `ci-deploy-bot`. */
|
|
663
|
+
agent?: string;
|
|
664
|
+
/** Protected action. Defaults to `production.deploy`. */
|
|
665
|
+
action?: typeof PRODUCTION_DEPLOY_ACTION | typeof DEPLOYMENT_PRODUCTION_ACTION | string;
|
|
666
|
+
/** Typed deploy gate context for `production.deploy`. */
|
|
667
|
+
context?: DeployGateContext | Record<string, unknown>;
|
|
668
|
+
}
|
|
669
|
+
/** Evidence metadata returned by {@link AtlaSentClient.deployGate}. */
|
|
670
|
+
interface DeployGateEvidence {
|
|
671
|
+
permitId?: string;
|
|
672
|
+
permitHash?: string;
|
|
673
|
+
auditHash?: string;
|
|
674
|
+
verifiedAt?: string;
|
|
675
|
+
}
|
|
676
|
+
/** Result of the canonical Deploy Gate V1 flow. */
|
|
677
|
+
interface DeployGateResponse {
|
|
678
|
+
/** True only after evaluate allowed AND `/v1-verify-permit` verified server-side. */
|
|
679
|
+
allowed: boolean;
|
|
680
|
+
/** Evaluation response from `POST /v1-evaluate`, when available. */
|
|
681
|
+
evaluation?: EvaluateResponse;
|
|
682
|
+
/** Verification response from `POST /v1-verify-permit`, when evaluation allowed. */
|
|
683
|
+
verification?: VerifyPermitResponse;
|
|
684
|
+
/** Human-readable block/allow reason. */
|
|
685
|
+
reason: string;
|
|
686
|
+
/** Best-effort audit/evidence metadata available to the SDK. */
|
|
687
|
+
evidence: DeployGateEvidence;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Frozen BVS snapshot wire shape (BI4).
|
|
691
|
+
* Carried in {@link EvaluateRequest}.context.bvsSnapshot when
|
|
692
|
+
* the `behavior_conditioning` flag is enabled for the tenant.
|
|
693
|
+
* Produced by behavior-insights GET /api/patterns/snapshot/:userId
|
|
694
|
+
* and attached via `@atlasent/behavior` attachToEvaluate().
|
|
695
|
+
*/
|
|
696
|
+
interface BvsSnapshot {
|
|
697
|
+
user_id: string;
|
|
698
|
+
/** Factor model output — keyed by BVS factor slug, value is score 0-1. */
|
|
699
|
+
factors: Record<string, number>;
|
|
700
|
+
/** Aggregate confidence score (0-1). Decays on a 60-day half-life. */
|
|
701
|
+
confidence: number;
|
|
702
|
+
/** True when the aggregate is fresh-and-thin (too few events to trust). */
|
|
703
|
+
confidence_low: boolean;
|
|
704
|
+
/** ISO-8601 timestamp of the compute run that produced this snapshot. */
|
|
705
|
+
computed_at: string;
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Consent-class projection (BI5) — the privacy-safe aggregate shape that
|
|
709
|
+
* third-party apps (LedgersMe, hiCoach, echobloom) receive when reading a
|
|
710
|
+
* user's behavioral summary. Counts and timestamps only; no raw free-text.
|
|
711
|
+
* Produced by behavior-insights `/api/patterns/summary/:userId` and fetched
|
|
712
|
+
* via `@atlasent/behavior` getStateSummary(). The SDK enforces
|
|
713
|
+
* {@link https://github.com/AtlaSent-Systems-Inc/atlasent-sdk | assertNoRawText}
|
|
714
|
+
* client-side before returning this shape to callers.
|
|
715
|
+
*/
|
|
716
|
+
interface ConsentClassProjection {
|
|
717
|
+
user_id: string;
|
|
718
|
+
window_start: string;
|
|
719
|
+
window_end: string;
|
|
720
|
+
event_count: number;
|
|
721
|
+
category_counts: Partial<Record<string, number>>;
|
|
722
|
+
}
|
|
723
|
+
/** Input to {@link AtlaSentClient.evaluate}. */
|
|
724
|
+
interface EvaluateRequest {
|
|
725
|
+
/** Identifier of the calling agent (e.g. "clinical-data-agent"). */
|
|
726
|
+
agent: string;
|
|
727
|
+
/** The action being authorized (e.g. "modify_patient_record"). */
|
|
728
|
+
action: string;
|
|
729
|
+
/** Arbitrary policy context (user, environment, resource IDs). */
|
|
730
|
+
context?: Record<string, unknown>;
|
|
731
|
+
/**
|
|
732
|
+
* When `true`, the server populates `riskEnvelope.factors` with a
|
|
733
|
+
* per-factor breakdown of the weighted risk score. Omit (or `false`)
|
|
734
|
+
* to keep response payloads small.
|
|
735
|
+
*/
|
|
736
|
+
explain?: boolean;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Slim permit object embedded in {@link EvaluateResponse} when the decision
|
|
740
|
+
* is `"allow"`. Contains the essential fields needed to act on the permit
|
|
741
|
+
* immediately without a separate `GET /v1/permits/:id` round-trip.
|
|
742
|
+
*
|
|
743
|
+
* Mirrors the `Permit` schema in atlasent-control-plane
|
|
744
|
+
* `api/src/schemas/permits.ts`.
|
|
745
|
+
*/
|
|
746
|
+
interface EvaluateResponsePermit {
|
|
747
|
+
id: string;
|
|
748
|
+
orgId: string;
|
|
749
|
+
subject: string;
|
|
750
|
+
scope: string;
|
|
751
|
+
status: "active" | "revoked" | "expired";
|
|
752
|
+
/** The evaluation that produced this permit. */
|
|
753
|
+
evaluationId: string | null;
|
|
754
|
+
issuedBy: string;
|
|
755
|
+
revokedBy: string | null;
|
|
756
|
+
/** ISO-8601 issuance timestamp. */
|
|
757
|
+
issuedAt: string;
|
|
758
|
+
revokedAt: string | null;
|
|
759
|
+
expiresAt: string | null;
|
|
760
|
+
metadata: Record<string, unknown> | null;
|
|
761
|
+
}
|
|
762
|
+
/** Result of {@link AtlaSentClient.evaluate}. */
|
|
763
|
+
interface EvaluateResponse {
|
|
764
|
+
/**
|
|
765
|
+
* Policy decision — canonical 4-value lowercase vocabulary:
|
|
766
|
+
* `"allow"`, `"deny"`, `"hold"`, or `"escalate"`.
|
|
767
|
+
*
|
|
768
|
+
* Previously emitted `"ALLOW"` / `"DENY"` (uppercase, 2-value);
|
|
769
|
+
* the SDK now normalises all values to lowercase and passes `hold`
|
|
770
|
+
* and `escalate` through rather than collapsing them to `"DENY"`.
|
|
771
|
+
*
|
|
772
|
+
* The `decision_canonical` field carries the same value and is the
|
|
773
|
+
* recommended field for new code.
|
|
774
|
+
*/
|
|
775
|
+
decision: Decision;
|
|
776
|
+
/**
|
|
777
|
+
* Canonical 4-value decision, byte-identical to the wire.
|
|
778
|
+
*
|
|
779
|
+
* One of `"allow"`, `"deny"`, `"hold"`, `"escalate"`. Branch on
|
|
780
|
+
* this field on new code. `hold` and `escalate` are non-terminal
|
|
781
|
+
* states that route to a human reviewer / approval signal — they
|
|
782
|
+
* are not equivalent to a `deny`.
|
|
783
|
+
*/
|
|
784
|
+
decision_canonical: DecisionCanonical;
|
|
785
|
+
/**
|
|
786
|
+
* Server-assigned identifier for this evaluation decision.
|
|
787
|
+
*
|
|
788
|
+
* Stable across retries and used as the key for proof retrieval
|
|
789
|
+
* (`GET /v1/proof/:evaluationId`) and override requests. Also
|
|
790
|
+
* available as the legacy `permitId` field for backward compatibility.
|
|
791
|
+
*/
|
|
792
|
+
evaluationId: string;
|
|
793
|
+
/** Opaque permit identifier, passed to {@link AtlaSentClient.verifyPermit}.
|
|
794
|
+
*
|
|
795
|
+
* @deprecated Prefer `evaluationId`. This field is kept for backward
|
|
796
|
+
* compatibility and points to the same server-assigned ID.
|
|
797
|
+
*/
|
|
798
|
+
permitId: string;
|
|
799
|
+
/**
|
|
800
|
+
* Slim permit object issued when `decision === "allow"`.
|
|
801
|
+
* `null` on deny, hold, or escalate decisions.
|
|
802
|
+
*
|
|
803
|
+
* Mirrors the `Permit` schema from the control-plane.
|
|
804
|
+
*/
|
|
805
|
+
permit: EvaluateResponsePermit | null;
|
|
806
|
+
/**
|
|
807
|
+
* Opaque HMAC-signed permit token issued when `decision === "allow"`.
|
|
808
|
+
* Pass to `POST /v1/verify-permit` to verify the permit server-side.
|
|
809
|
+
* `null` on deny, hold, or escalate decisions.
|
|
810
|
+
*/
|
|
811
|
+
permitToken: string | null;
|
|
812
|
+
/**
|
|
813
|
+
* Machine-readable reasons emitted by the policy engine.
|
|
814
|
+
*
|
|
815
|
+
* The array may be empty. For deny/hold/escalate decisions the array
|
|
816
|
+
* typically contains a single human-readable explanation; for allow
|
|
817
|
+
* decisions it is often empty. Do not parse these strings — use
|
|
818
|
+
* `decision` for branching.
|
|
819
|
+
*/
|
|
820
|
+
reasons: string[];
|
|
821
|
+
/** Human-readable explanation from the policy engine.
|
|
822
|
+
*
|
|
823
|
+
* @deprecated Prefer `reasons[0]` or `reasons`. This field is the
|
|
824
|
+
* first element of `reasons` (or an empty string) for backward compat.
|
|
825
|
+
*/
|
|
826
|
+
reason: string;
|
|
827
|
+
/** Hash-chained audit-trail entry (21 CFR Part 11 / GxP-ready). */
|
|
828
|
+
auditHash: string;
|
|
829
|
+
/** ISO 8601 timestamp of the decision. */
|
|
830
|
+
timestamp: string;
|
|
831
|
+
/**
|
|
832
|
+
* Per-key rate-limit state for this request's response, parsed from
|
|
833
|
+
* `X-RateLimit-*` headers. `null` when the server didn't emit them.
|
|
834
|
+
*/
|
|
835
|
+
rateLimit: RateLimitState | null;
|
|
836
|
+
/**
|
|
837
|
+
* Risk envelope summary from the policy engine. Present on all responses
|
|
838
|
+
* from engine version wire-v1@1.0.0+. Provides the weighted risk score,
|
|
839
|
+
* the pre/post-promotion decisions, and (when evaluate was called with
|
|
840
|
+
* `explain: true`) a per-factor breakdown.
|
|
841
|
+
*
|
|
842
|
+
* The envelope can only raise severity — it structurally cannot soften
|
|
843
|
+
* a deny to allow. When `promoted` is true the live `decision` was
|
|
844
|
+
* upgraded from `engineDecision` to `envelopeDecision`.
|
|
845
|
+
*/
|
|
846
|
+
riskEnvelope?: EvaluateRiskEnvelope;
|
|
847
|
+
}
|
|
848
|
+
/** Per-factor contribution in a {@link EvaluateRiskEnvelope}. */
|
|
849
|
+
interface EvaluateRiskEnvelopeFactor {
|
|
850
|
+
/** Factor identifier, e.g. `"ACTION_SENSITIVITY"`. */
|
|
851
|
+
factor: string;
|
|
852
|
+
/** Factor score in [0, 1]. Higher = more risk. */
|
|
853
|
+
value: number;
|
|
854
|
+
/** Configured weight for this factor. */
|
|
855
|
+
weight: number;
|
|
856
|
+
/** Human-readable explanation for the score. */
|
|
857
|
+
reason: string;
|
|
858
|
+
}
|
|
859
|
+
/** Risk envelope summary returned in a top-level {@link EvaluateResponse}. */
|
|
860
|
+
interface EvaluateRiskEnvelope {
|
|
861
|
+
/** Weighted risk score in [0, 1]. Score ≥ 0.70 triggers a hold. */
|
|
862
|
+
weightedScore: number;
|
|
863
|
+
/** Policy engine decision before envelope promotion. */
|
|
864
|
+
engineDecision: Decision;
|
|
865
|
+
/** Decision resolved by the risk envelope. */
|
|
866
|
+
envelopeDecision: Decision;
|
|
867
|
+
/** `true` when the envelope raised the decision's severity (most-restrictive-wins). */
|
|
868
|
+
promoted: boolean;
|
|
869
|
+
/** Deny codes that unconditionally block regardless of score. */
|
|
870
|
+
hardBlocks: string[];
|
|
871
|
+
/** Per-factor breakdown. Present only when `explain: true` was passed. */
|
|
872
|
+
factors?: EvaluateRiskEnvelopeFactor[];
|
|
873
|
+
}
|
|
874
|
+
/** Input to {@link AtlaSentClient.verifyPermit}. */
|
|
875
|
+
interface VerifyPermitRequest {
|
|
876
|
+
/** The permit ID returned by a prior evaluate() call. */
|
|
877
|
+
permitId: string;
|
|
878
|
+
/** Optional: re-state the action for cross-check with the server. */
|
|
879
|
+
action?: string;
|
|
880
|
+
/** Optional: re-state the agent for cross-check with the server. */
|
|
881
|
+
agent?: string;
|
|
882
|
+
/** Optional: re-state the context for cross-check with the server. */
|
|
883
|
+
context?: Record<string, unknown>;
|
|
884
|
+
/**
|
|
885
|
+
* Environment of the permit being verified. Sourced from the evaluate
|
|
886
|
+
* payload (context.environment → top-level environment → "production").
|
|
887
|
+
* Required by the server for production permits as of 2026-05-14.
|
|
888
|
+
* P1-1 fix: withPermit/protect now always populates this field.
|
|
889
|
+
*/
|
|
890
|
+
environment?: string;
|
|
891
|
+
/**
|
|
892
|
+
* SHA-256 hex digest of the recursively key-sorted canonical JSON of the
|
|
893
|
+
* original evaluate payload. Required by the server for production permits
|
|
894
|
+
* as of 2026-05-14.
|
|
895
|
+
* P1-5 fix: withPermit/protect now always computes and sends this field.
|
|
896
|
+
*/
|
|
897
|
+
execution_hash?: string;
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Result of {@link AtlaSentClient.verifyPermit}.
|
|
901
|
+
*
|
|
902
|
+
* @deprecated Use {@link VerifyPermitByIdResponse} via
|
|
903
|
+
* {@link AtlaSentClient.verifyPermitById} — the canonical REST surface
|
|
904
|
+
* (`POST /v1/permits/{id}/verify`) returns the unified verification
|
|
905
|
+
* envelope (`valid`, `verification_type`, `reason`, `verified_at`,
|
|
906
|
+
* `evidence`) plus the full {@link PermitRecord} fields. Will be
|
|
907
|
+
* removed in `@atlasent/sdk@3`.
|
|
908
|
+
*/
|
|
909
|
+
interface VerifyPermitResponse {
|
|
910
|
+
/** `true` when the permit is valid and un-revoked. */
|
|
911
|
+
verified: boolean;
|
|
912
|
+
/** Verification outcome string from the server. */
|
|
913
|
+
outcome: string;
|
|
914
|
+
/** Verification hash bound to the permit. */
|
|
915
|
+
permitHash: string;
|
|
916
|
+
/** ISO 8601 timestamp of the verification. */
|
|
917
|
+
timestamp: string;
|
|
918
|
+
/**
|
|
919
|
+
* ISO-8601 expiration timestamp of the permit. `null` on pre-rollout
|
|
920
|
+
* server versions that do not yet surface this field.
|
|
921
|
+
*/
|
|
922
|
+
expiresAt: string | null;
|
|
923
|
+
/**
|
|
924
|
+
* Per-key rate-limit state for this request's response, parsed from
|
|
925
|
+
* `X-RateLimit-*` headers. `null` when the server didn't emit them.
|
|
926
|
+
*/
|
|
927
|
+
rateLimit: RateLimitState | null;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Result of {@link AtlaSentClient.keySelf} — self-introspection of the API
|
|
931
|
+
* key the client was constructed with. Returned by `GET /v1/api-key-self`.
|
|
932
|
+
*
|
|
933
|
+
* Never includes the raw key or its hash — introspection is intentionally
|
|
934
|
+
* read-only and safe to surface in operator dashboards. Useful for:
|
|
935
|
+
* - "which key am I?" debugging
|
|
936
|
+
* - IP_NOT_ALLOWED failures — `clientIp` is the IP the server observed
|
|
937
|
+
* - proactive expiry warnings — `expiresAt` is the server-stored expiry
|
|
938
|
+
* (`null` means the key does not auto-expire)
|
|
939
|
+
* - verifying scopes before attempting a scope-gated action
|
|
940
|
+
*/
|
|
941
|
+
interface ApiKeySelfResponse {
|
|
942
|
+
/** Server-side UUID of the api_keys row for this key. */
|
|
943
|
+
keyId: string;
|
|
944
|
+
/** Organization the key belongs to. */
|
|
945
|
+
organizationId: string;
|
|
946
|
+
/** "live" or "test" (or any future environment label the server introduces). */
|
|
947
|
+
environment: string;
|
|
948
|
+
/** Granted scopes — e.g. ["evaluate", "audit.read"]. */
|
|
949
|
+
scopes: string[];
|
|
950
|
+
/**
|
|
951
|
+
* Per-key IP allowlist as CIDR strings (e.g. ["10.0.0.0/8"]). `null`
|
|
952
|
+
* when the key is unrestricted.
|
|
953
|
+
*/
|
|
954
|
+
allowedCidrs: string[] | null;
|
|
955
|
+
/** Server-enforced per-minute rate limit for this key. */
|
|
956
|
+
rateLimitPerMinute: number;
|
|
957
|
+
/** Client IP as the server observed it (first hop of X-Forwarded-For). */
|
|
958
|
+
clientIp: string | null;
|
|
959
|
+
/** Server-stored expiry; `null` means the key does not auto-expire. */
|
|
960
|
+
expiresAt: string | null;
|
|
961
|
+
/**
|
|
962
|
+
* Per-key rate-limit state for this request's response, parsed from
|
|
963
|
+
* `X-RateLimit-*` headers. `null` when the server didn't emit them.
|
|
964
|
+
*/
|
|
965
|
+
rateLimit: RateLimitState | null;
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Result of {@link AtlaSentClient.listAuditEvents}. Extends the raw
|
|
969
|
+
* wire page with a camelCase `rateLimit` alongside the snake_case
|
|
970
|
+
* wire fields — the wire shape (`events`, `total`, `next_cursor`) is
|
|
971
|
+
* untouched so callers that pass it to the offline verifier get
|
|
972
|
+
* byte-identical behaviour.
|
|
973
|
+
*/
|
|
974
|
+
interface AuditEventsResult extends AuditEventsPage {
|
|
975
|
+
/**
|
|
976
|
+
* Per-key rate-limit state for this request's response, parsed from
|
|
977
|
+
* `X-RateLimit-*` headers. `null` when the server didn't emit them.
|
|
978
|
+
*/
|
|
979
|
+
rateLimit: RateLimitState | null;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Filter accepted by {@link AtlaSentClient.createAuditExport}. Fields
|
|
983
|
+
* are snake_case to match the server's `POST /v1-audit/exports`
|
|
984
|
+
* request body; an empty object requests a full-org bundle.
|
|
985
|
+
*/
|
|
986
|
+
interface AuditExportRequest {
|
|
987
|
+
/** Comma-joined list of event types to include (e.g. `"evaluate.allow,policy.updated"`). */
|
|
988
|
+
types?: string;
|
|
989
|
+
/** Filter to a single actor. */
|
|
990
|
+
actor_id?: string;
|
|
991
|
+
/** Inclusive lower bound on `occurred_at` (ISO 8601). */
|
|
992
|
+
from?: string;
|
|
993
|
+
/** Inclusive upper bound on `occurred_at` (ISO 8601). */
|
|
994
|
+
to?: string;
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Result of {@link AtlaSentClient.createAuditExport}. Extends the
|
|
998
|
+
* signed bundle shape with a camelCase `rateLimit`. The signed
|
|
999
|
+
* envelope fields (`export_id`, `org_id`, `chain_head_hash`,
|
|
1000
|
+
* `event_count`, `signed_at`, `events`, `signature`) are preserved
|
|
1001
|
+
* byte-for-byte so the object can be handed straight to
|
|
1002
|
+
* `verifyAuditBundle(bundle, keys)`.
|
|
1003
|
+
*/
|
|
1004
|
+
interface AuditExportResult extends AuditExport {
|
|
1005
|
+
/**
|
|
1006
|
+
* Per-key rate-limit state for this request's response, parsed from
|
|
1007
|
+
* `X-RateLimit-*` headers. `null` when the server didn't emit them.
|
|
1008
|
+
*/
|
|
1009
|
+
rateLimit: RateLimitState | null;
|
|
1010
|
+
}
|
|
1011
|
+
/** Constructor options for {@link AtlaSentClient}. */
|
|
1012
|
+
interface AtlaSentClientOptions {
|
|
1013
|
+
/** Required. Your AtlaSent API key. */
|
|
1014
|
+
apiKey: string;
|
|
1015
|
+
/** API base URL. Defaults to "https://api.atlasent.io". */
|
|
1016
|
+
baseUrl?: string;
|
|
1017
|
+
/** Per-request timeout in milliseconds. Defaults to 10_000. */
|
|
1018
|
+
timeoutMs?: number;
|
|
1019
|
+
/**
|
|
1020
|
+
* Inject a fetch implementation (primarily for testing).
|
|
1021
|
+
* Defaults to `globalThis.fetch`.
|
|
1022
|
+
*/
|
|
1023
|
+
fetch?: typeof fetch;
|
|
1024
|
+
/**
|
|
1025
|
+
* Retry policy for transient failures (network errors, timeouts,
|
|
1026
|
+
* 429 rate-limit, 5xx server errors, malformed responses).
|
|
1027
|
+
* Omit to use the default: 4 total attempts, 2 000 ms base, 16 000 ms cap,
|
|
1028
|
+
* full-jitter exponential backoff matching the Python SDK schedule
|
|
1029
|
+
* (2 s → 4 s → 8 s → 16 s).
|
|
1030
|
+
* Pass `{ maxAttempts: 1 }` to disable retries entirely.
|
|
1031
|
+
*/
|
|
1032
|
+
retryPolicy?: RetryPolicy;
|
|
1033
|
+
}
|
|
1034
|
+
/** Permit lifecycle status. */
|
|
1035
|
+
type PermitStatus = "issued" | "verified" | "consumed" | "expired" | "revoked";
|
|
1036
|
+
/**
|
|
1037
|
+
* Wire shape of a Permit row, returned by {@link AtlaSentClient.getPermit}
|
|
1038
|
+
* and {@link AtlaSentClient.listPermits}. Mirrors the openapi `Permit`
|
|
1039
|
+
* schema.
|
|
1040
|
+
*
|
|
1041
|
+
* Revocation fields (`revoked_at`, `revoked_by`, `revoke_reason`) are
|
|
1042
|
+
* populated only when `status === 'revoked'`; null otherwise.
|
|
1043
|
+
*/
|
|
1044
|
+
interface PermitRecord {
|
|
1045
|
+
id: string;
|
|
1046
|
+
org_id: string;
|
|
1047
|
+
actor_id: string;
|
|
1048
|
+
action_id: string;
|
|
1049
|
+
target_id?: string;
|
|
1050
|
+
environment?: string;
|
|
1051
|
+
status: PermitStatus;
|
|
1052
|
+
issued_at: string;
|
|
1053
|
+
expires_at: string;
|
|
1054
|
+
consumed_at?: string | null;
|
|
1055
|
+
revoked_at?: string | null;
|
|
1056
|
+
revoked_by?: string | null;
|
|
1057
|
+
revoke_reason?: string | null;
|
|
1058
|
+
signature?: string;
|
|
1059
|
+
payload_hash?: string | null;
|
|
1060
|
+
decision_id?: string | null;
|
|
1061
|
+
}
|
|
1062
|
+
/** Optional filters for {@link AtlaSentClient.listPermits}. */
|
|
1063
|
+
interface ListPermitsRequest {
|
|
1064
|
+
status?: PermitStatus;
|
|
1065
|
+
actorId?: string;
|
|
1066
|
+
actionType?: string;
|
|
1067
|
+
/** ISO-8601 lower bound on `created_at`. */
|
|
1068
|
+
from?: string;
|
|
1069
|
+
/** ISO-8601 upper bound on `created_at`. */
|
|
1070
|
+
to?: string;
|
|
1071
|
+
/** Page size. Server max is 500; default 50. */
|
|
1072
|
+
limit?: number;
|
|
1073
|
+
/** Pass `nextCursor` from a prior response to page forward. */
|
|
1074
|
+
cursor?: string;
|
|
1075
|
+
}
|
|
1076
|
+
/** Response from {@link AtlaSentClient.listPermits}. */
|
|
1077
|
+
interface ListPermitsResponse {
|
|
1078
|
+
permits: PermitRecord[];
|
|
1079
|
+
/** Total matching rows ignoring `limit`/`cursor`. */
|
|
1080
|
+
total: number;
|
|
1081
|
+
/** Pass on next call as `cursor`. Absent when no more rows. */
|
|
1082
|
+
nextCursor?: string;
|
|
1083
|
+
rateLimit: RateLimitState | null;
|
|
1084
|
+
}
|
|
1085
|
+
/** Response from {@link AtlaSentClient.getPermit}. */
|
|
1086
|
+
interface GetPermitResponse {
|
|
1087
|
+
permit: PermitRecord;
|
|
1088
|
+
rateLimit: RateLimitState | null;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Response from {@link AtlaSentClient.checkPermitValid}.
|
|
1092
|
+
*
|
|
1093
|
+
* Lightweight validity snapshot returned by
|
|
1094
|
+
* `GET /v1/permits/{permitId}/valid`. Designed for guard heartbeat
|
|
1095
|
+
* polling — returns only the fields needed to determine whether to
|
|
1096
|
+
* abort a running permit mid-execution (via {@link PermitRevoked}).
|
|
1097
|
+
*/
|
|
1098
|
+
interface PermitValidResponse {
|
|
1099
|
+
/** True iff the permit is currently valid (active). */
|
|
1100
|
+
valid: boolean;
|
|
1101
|
+
/**
|
|
1102
|
+
* Current lifecycle status of the permit.
|
|
1103
|
+
* - `"active"` — permit is valid and in-flight.
|
|
1104
|
+
* - `"expired"` — TTL elapsed before revocation or consumption.
|
|
1105
|
+
* - `"revoked"` — administratively revoked (see `revocation_id`).
|
|
1106
|
+
* - `"consumed"` — single-use permit already consumed.
|
|
1107
|
+
*/
|
|
1108
|
+
status: "active" | "expired" | "revoked" | "consumed";
|
|
1109
|
+
/** ISO-8601 timestamp when the permit was revoked. Populated only when `status === "revoked"`. */
|
|
1110
|
+
revoked_at?: string;
|
|
1111
|
+
/** Opaque identifier of the revocation record. Populated only when `status === "revoked"`. */
|
|
1112
|
+
revocation_id?: string;
|
|
1113
|
+
}
|
|
1114
|
+
/** Input for {@link AtlaSentClient.revokePermitById}. */
|
|
1115
|
+
interface RevokePermitByIdInput {
|
|
1116
|
+
/** Operator-supplied free-text reason. Recorded on the permit row,
|
|
1117
|
+
* written to the audit trail, and surfaced (truncated) on later
|
|
1118
|
+
* verify responses. Optional but strongly encouraged. */
|
|
1119
|
+
reason?: string;
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Response from {@link AtlaSentClient.revokePermitById}.
|
|
1123
|
+
*
|
|
1124
|
+
* Returns the updated {@link PermitRecord} with `status === 'revoked'`
|
|
1125
|
+
* and the populated `revoked_at` / `revoked_by` / `revoke_reason`
|
|
1126
|
+
* fields.
|
|
1127
|
+
*/
|
|
1128
|
+
interface RevokePermitByIdResponse {
|
|
1129
|
+
permit: PermitRecord;
|
|
1130
|
+
rateLimit: RateLimitState | null;
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Response from {@link AtlaSentClient.verifyPermitById}.
|
|
1134
|
+
*
|
|
1135
|
+
* Returns the canonical verification envelope (`valid`,
|
|
1136
|
+
* `verification_type`, `reason`, `verified_at`, `evidence`) plus the
|
|
1137
|
+
* legacy {@link PermitRecord} fields preserved at the top level for
|
|
1138
|
+
* backward compatibility. The envelope shape matches the unified
|
|
1139
|
+
* verify response in atlasent-api PR #352.
|
|
1140
|
+
*/
|
|
1141
|
+
interface VerifyPermitByIdResponse {
|
|
1142
|
+
/** `true` iff the permit verified — i.e. unconsumed, unexpired, and signature OK. */
|
|
1143
|
+
valid: boolean;
|
|
1144
|
+
/** Always `'permit'` on this surface. */
|
|
1145
|
+
verification_type: "permit";
|
|
1146
|
+
/** Operator-readable explanation when `valid` is `false`; `null` on success. */
|
|
1147
|
+
reason: string | null;
|
|
1148
|
+
/** Server clock at the moment verification ran. */
|
|
1149
|
+
verified_at: string;
|
|
1150
|
+
/** Type-specific evidence — same fields as the openapi PermitVerifyEvidence schema. */
|
|
1151
|
+
evidence: {
|
|
1152
|
+
permit_id: string;
|
|
1153
|
+
status: PermitStatus;
|
|
1154
|
+
actor_id?: string;
|
|
1155
|
+
action_id?: string;
|
|
1156
|
+
expires_at?: string;
|
|
1157
|
+
payload_hash?: string | null;
|
|
1158
|
+
decision_id?: string | null;
|
|
1159
|
+
};
|
|
1160
|
+
/** Legacy: full permit row preserved at the top level. */
|
|
1161
|
+
permit: PermitRecord;
|
|
1162
|
+
rateLimit: RateLimitState | null;
|
|
1163
|
+
}
|
|
1164
|
+
/** Input for {@link AtlaSentClient.revokePermit}. */
|
|
1165
|
+
interface RevokePermitRequest {
|
|
1166
|
+
/** The permit ID returned by a prior evaluate() call. */
|
|
1167
|
+
permitId: string;
|
|
1168
|
+
/** Optional human-readable reason stored in the audit log. */
|
|
1169
|
+
reason?: string;
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Result of {@link AtlaSentClient.revokePermit}.
|
|
1173
|
+
*
|
|
1174
|
+
* @deprecated Use {@link RevokePermitByIdResponse} via
|
|
1175
|
+
* {@link AtlaSentClient.revokePermitById} — the canonical REST surface
|
|
1176
|
+
* (`POST /v1/permits/{id}/revoke`) returns the full updated
|
|
1177
|
+
* {@link PermitRecord} with `revoked_at`/`revoked_by`/`revoke_reason`
|
|
1178
|
+
* populated, instead of the legacy `{revoked, permitId}` envelope.
|
|
1179
|
+
* Will be removed in `@atlasent/sdk@3`.
|
|
1180
|
+
*/
|
|
1181
|
+
interface RevokePermitResponse {
|
|
1182
|
+
/** `true` when the permit was found and successfully revoked. */
|
|
1183
|
+
revoked: boolean;
|
|
1184
|
+
/** Echo of the revoked permit's ID. */
|
|
1185
|
+
permitId: string;
|
|
1186
|
+
/** ISO-8601 timestamp of when the revocation was recorded. `undefined` when not returned by the server. */
|
|
1187
|
+
revokedAt?: string | undefined;
|
|
1188
|
+
/** Audit hash for the revocation event. `undefined` when not returned by the server. */
|
|
1189
|
+
auditHash?: string | undefined;
|
|
1190
|
+
/** Per-key rate-limit state. `null` when the server didn't emit headers. */
|
|
1191
|
+
rateLimit: RateLimitState | null;
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* One stage of a single policy's constraint evaluation.
|
|
1195
|
+
*
|
|
1196
|
+
* Mirrors `ConstraintTraceStage` in
|
|
1197
|
+
* `atlasent-api/packages/types/src/index.ts`. Emitted by the rule
|
|
1198
|
+
* engine when the request URL carries `?include=constraint_trace`.
|
|
1199
|
+
*
|
|
1200
|
+
* Forward-compat: extra engine-side keys are tolerated; readers
|
|
1201
|
+
* should not assume this is a closed shape.
|
|
1202
|
+
*/
|
|
1203
|
+
interface ConstraintTraceStage {
|
|
1204
|
+
/** Engine stage name (e.g. `"role_check"`, `"context"`). */
|
|
1205
|
+
readonly stage: string;
|
|
1206
|
+
/** Optional rule identifier; absent for wrapper stages. */
|
|
1207
|
+
readonly rule?: string;
|
|
1208
|
+
/** True iff this stage's predicate fired. */
|
|
1209
|
+
readonly matched: boolean;
|
|
1210
|
+
/** Optional human-readable note from the engine. */
|
|
1211
|
+
readonly detail?: string;
|
|
1212
|
+
/** Zero-based position within the policy's `stages` array. */
|
|
1213
|
+
readonly order: number;
|
|
1214
|
+
/** Forward-compat: tolerate unknown engine-side keys without crashing. */
|
|
1215
|
+
readonly [key: string]: unknown;
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Per-policy block of a constraint trace.
|
|
1219
|
+
*
|
|
1220
|
+
* Mirrors `ConstraintTracePolicy` in
|
|
1221
|
+
* `atlasent-api/packages/types/src/index.ts`. The handler iterates
|
|
1222
|
+
* active policies in order until first non-allow; the policy that
|
|
1223
|
+
* produced the outer decision has `decision !== "allow"`.
|
|
1224
|
+
*/
|
|
1225
|
+
interface ConstraintTracePolicy {
|
|
1226
|
+
/** Stable identifier of the evaluated policy. */
|
|
1227
|
+
readonly policy_id: string;
|
|
1228
|
+
/** Policy-level decision (`"allow"|"deny"|"hold"|"escalate"`). */
|
|
1229
|
+
readonly decision: string;
|
|
1230
|
+
/** Engine-side fingerprint of the bundle row. */
|
|
1231
|
+
readonly fingerprint: string;
|
|
1232
|
+
/**
|
|
1233
|
+
* Optional engine-computed risk score from a `risk` rule clause.
|
|
1234
|
+
* Distinct from the heuristic risk score on the outer envelope.
|
|
1235
|
+
*/
|
|
1236
|
+
readonly risk_score?: number;
|
|
1237
|
+
/** Ordered stages produced while evaluating this policy. */
|
|
1238
|
+
readonly stages: ReadonlyArray<ConstraintTraceStage>;
|
|
1239
|
+
/** Forward-compat: tolerate unknown engine-side keys. */
|
|
1240
|
+
readonly [key: string]: unknown;
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Top-level constraint trace returned by
|
|
1244
|
+
* `/v1-evaluate?include=constraint_trace`.
|
|
1245
|
+
*
|
|
1246
|
+
* Mirrors `ConstraintTraceResponse` in
|
|
1247
|
+
* `atlasent-api/packages/types/src/index.ts`. Present iff the
|
|
1248
|
+
* caller requested the trace; the SDK's preflight helper always
|
|
1249
|
+
* requests it.
|
|
1250
|
+
*/
|
|
1251
|
+
interface ConstraintTrace {
|
|
1252
|
+
/** Per-policy blocks in evaluation order. */
|
|
1253
|
+
readonly rules_evaluated: ReadonlyArray<ConstraintTracePolicy>;
|
|
1254
|
+
/**
|
|
1255
|
+
* Policy id whose evaluation produced the outer decision. Equals
|
|
1256
|
+
* the outer `matched_policy_id` on non-allow paths; `undefined` on
|
|
1257
|
+
* a clean allow (all policies passed).
|
|
1258
|
+
*/
|
|
1259
|
+
readonly matching_policy_id?: string;
|
|
1260
|
+
/** Forward-compat: tolerate unknown engine-side keys. */
|
|
1261
|
+
readonly [key: string]: unknown;
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Result of {@link AtlaSentClient.evaluatePreflight}.
|
|
1265
|
+
*
|
|
1266
|
+
* Wraps the regular {@link EvaluateResponse} plus the
|
|
1267
|
+
* {@link ConstraintTrace} returned when the request URL carries
|
|
1268
|
+
* `?include=constraint_trace`. The whole point of preflight is to
|
|
1269
|
+
* surface which stages / policies WOULD fire BEFORE pushing the
|
|
1270
|
+
* request onto an approval queue, so workflows can reject trivially
|
|
1271
|
+
* defective requests at submission time and only forward viable
|
|
1272
|
+
* requests to a human reviewer.
|
|
1273
|
+
*
|
|
1274
|
+
* `constraintTrace` is `null` on responses from older atlasent-api
|
|
1275
|
+
* deployments that do not echo the trace — forward-compatible
|
|
1276
|
+
* degradation.
|
|
1277
|
+
*/
|
|
1278
|
+
interface EvaluatePreflightResponse {
|
|
1279
|
+
/** The regular evaluate response (decision, permitId, ...). */
|
|
1280
|
+
readonly evaluation: EvaluateResponse;
|
|
1281
|
+
/**
|
|
1282
|
+
* The constraint trace, or `null` when the server omitted it
|
|
1283
|
+
* (older atlasent-api version).
|
|
1284
|
+
*/
|
|
1285
|
+
readonly constraintTrace: ConstraintTrace | null;
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Options for {@link AtlaSentClient.protectStream}.
|
|
1289
|
+
*
|
|
1290
|
+
* All fields are optional; defaults are used when omitted.
|
|
1291
|
+
*/
|
|
1292
|
+
interface StreamOptions {
|
|
1293
|
+
/**
|
|
1294
|
+
* Optional abort signal to cancel the stream from the caller side.
|
|
1295
|
+
*/
|
|
1296
|
+
signal?: AbortSignal;
|
|
1297
|
+
/**
|
|
1298
|
+
* Per-event timeout in milliseconds: if no SSE event arrives within
|
|
1299
|
+
* this window the stream throws {@link StreamTimeoutError}.
|
|
1300
|
+
* Defaults to 30 000 ms (30 s). Pass `0` to disable.
|
|
1301
|
+
*/
|
|
1302
|
+
timeoutMs?: number;
|
|
1303
|
+
/**
|
|
1304
|
+
* Maximum reconnection attempts on network drop before the stream
|
|
1305
|
+
* gives up and throws. Defaults to 3.
|
|
1306
|
+
*/
|
|
1307
|
+
maxRetries?: number;
|
|
1308
|
+
}
|
|
1309
|
+
/** A policy decision emitted mid-stream. */
|
|
1310
|
+
interface StreamDecisionEvent {
|
|
1311
|
+
type: "decision";
|
|
1312
|
+
/**
|
|
1313
|
+
* Policy decision — canonical 4-value lowercase vocabulary:
|
|
1314
|
+
* `"allow"`, `"deny"`, `"hold"`, or `"escalate"`.
|
|
1315
|
+
*
|
|
1316
|
+
* Previously emitted `"ALLOW"` / `"DENY"` (uppercase, 2-value);
|
|
1317
|
+
* now unified with `decision_canonical`.
|
|
1318
|
+
*
|
|
1319
|
+
* @deprecated Read `decision_canonical` instead for forward-compatible
|
|
1320
|
+
* branching. Both fields now carry the same value. Will be
|
|
1321
|
+
* removed/changed in `@atlasent/sdk@3`.
|
|
1322
|
+
*/
|
|
1323
|
+
decision: Decision;
|
|
1324
|
+
/**
|
|
1325
|
+
* Canonical 4-value decision, byte-identical to the wire.
|
|
1326
|
+
* One of `"allow"`, `"deny"`, `"hold"`, `"escalate"`.
|
|
1327
|
+
*/
|
|
1328
|
+
decision_canonical: DecisionCanonical;
|
|
1329
|
+
/** Opaque permit identifier for a final allow. Pass to verifyPermit. */
|
|
1330
|
+
permitId: string;
|
|
1331
|
+
/** Human-readable explanation from the policy engine. */
|
|
1332
|
+
reason: string;
|
|
1333
|
+
/** Audit hash bound to this decision. */
|
|
1334
|
+
auditHash: string;
|
|
1335
|
+
/** ISO-8601 timestamp of the decision. */
|
|
1336
|
+
timestamp: string;
|
|
1337
|
+
/** When true the stream will emit done and close after this event. */
|
|
1338
|
+
isFinal: boolean;
|
|
1339
|
+
}
|
|
1340
|
+
/** An intermediate progress hint emitted before the final decision. */
|
|
1341
|
+
interface StreamProgressEvent {
|
|
1342
|
+
type: "progress";
|
|
1343
|
+
/** Human-readable stage name (e.g. "policy_loading", "context_enrichment"). */
|
|
1344
|
+
stage: string;
|
|
1345
|
+
/** Additional server-defined fields — forward-compat, do not rely on shape. */
|
|
1346
|
+
[key: string]: unknown;
|
|
1347
|
+
}
|
|
1348
|
+
/** Union of all events yielded by {@link AtlaSentClient.protectStream}. */
|
|
1349
|
+
type StreamEvent = StreamDecisionEvent | StreamProgressEvent;
|
|
1350
|
+
/**
|
|
1351
|
+
* A single item in a {@link AtlaSentClient.evaluateBatch} call.
|
|
1352
|
+
* Same shape as {@link EvaluateRequest}.
|
|
1353
|
+
*/
|
|
1354
|
+
interface BatchEvalItem {
|
|
1355
|
+
/** Identifier of the calling agent. */
|
|
1356
|
+
agent: string;
|
|
1357
|
+
/** The action being authorized. */
|
|
1358
|
+
action: string;
|
|
1359
|
+
/** Arbitrary policy context. */
|
|
1360
|
+
context?: Record<string, unknown>;
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Per-item result in an {@link EvaluateBatchResponse}.
|
|
1364
|
+
*
|
|
1365
|
+
* Success items carry `decision`, `decisionId`, `permitToken`, `auditHash`,
|
|
1366
|
+
* and `timestamp`. Error items (when the per-item RPC layer failed) carry
|
|
1367
|
+
* only `index`, `error`, and optionally `message`.
|
|
1368
|
+
*/
|
|
1369
|
+
interface EvaluateBatchResultItem {
|
|
1370
|
+
/** 0-based position matching the input order. */
|
|
1371
|
+
index: number;
|
|
1372
|
+
/**
|
|
1373
|
+
* Policy decision for this item. Present on success items.
|
|
1374
|
+
* `"allow"`, `"deny"`, `"hold"`, or `"escalate"`.
|
|
1375
|
+
*/
|
|
1376
|
+
decision?: DecisionCanonical;
|
|
1377
|
+
/** Server-assigned permit / decision identifier. */
|
|
1378
|
+
decisionId?: string;
|
|
1379
|
+
/** Opaque permit token (allow decisions only). Pass to verifyPermit(). */
|
|
1380
|
+
permitToken?: string | null;
|
|
1381
|
+
/** Machine-readable denial / hold reason. */
|
|
1382
|
+
reason?: string;
|
|
1383
|
+
/** Hash-chained audit-trail entry. */
|
|
1384
|
+
auditHash?: string;
|
|
1385
|
+
/** ISO-8601 decision timestamp. */
|
|
1386
|
+
timestamp?: string;
|
|
1387
|
+
/** Error code when the item itself failed at the RPC layer. */
|
|
1388
|
+
error?: string;
|
|
1389
|
+
/** Human-readable detail when `error` is set. */
|
|
1390
|
+
message?: string;
|
|
1391
|
+
}
|
|
1392
|
+
/**
|
|
1393
|
+
* Response from {@link AtlaSentClient.evaluateBatch}.
|
|
1394
|
+
*
|
|
1395
|
+
* - `items` is in the same order as the input `requests` array.
|
|
1396
|
+
* - `partial: true` means at least one item errored at the RPC layer
|
|
1397
|
+
* (not a policy deny — those are surfaced via `decision: "deny"` on
|
|
1398
|
+
* the item). Check `item.error` on items without a `decision`.
|
|
1399
|
+
* - `replayed: true` means the response was served from the idempotency
|
|
1400
|
+
* cache (a prior call with the same `batchId` completed within 24 h).
|
|
1401
|
+
*/
|
|
1402
|
+
interface BatchEvalResponse {
|
|
1403
|
+
/** Server-assigned (or caller-supplied) batch identifier. */
|
|
1404
|
+
batchId: string;
|
|
1405
|
+
/** Per-item results, in input order. */
|
|
1406
|
+
items: EvaluateBatchResultItem[];
|
|
1407
|
+
/** `true` when at least one item failed at the RPC layer. */
|
|
1408
|
+
partial: boolean;
|
|
1409
|
+
/** `true` when served from the idempotency cache. */
|
|
1410
|
+
replayed?: boolean;
|
|
1411
|
+
/** Rate-limit state from the batch response headers. */
|
|
1412
|
+
rateLimit: RateLimitState | null;
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* Options for {@link AtlaSentClient.subscribeDecisions}.
|
|
1416
|
+
*/
|
|
1417
|
+
interface SubscribeDecisionsOptions {
|
|
1418
|
+
/**
|
|
1419
|
+
* Filter to specific event types (e.g. `["evaluate.allow", "evaluate.deny"]`).
|
|
1420
|
+
* Omit to receive all types.
|
|
1421
|
+
*/
|
|
1422
|
+
types?: string[];
|
|
1423
|
+
/** Filter to a specific actor ID. */
|
|
1424
|
+
actorId?: string;
|
|
1425
|
+
/**
|
|
1426
|
+
* Resume from a prior event. Pass the `id` of the last received event.
|
|
1427
|
+
* The server replays everything after that sequence position, then
|
|
1428
|
+
* transitions to live polling.
|
|
1429
|
+
*/
|
|
1430
|
+
lastEventId?: string;
|
|
1431
|
+
/**
|
|
1432
|
+
* Maximum session duration in seconds. The server emits `session_end`
|
|
1433
|
+
* and closes after this window; the caller should reconnect with the
|
|
1434
|
+
* last received `lastEventId`. Defaults to 1800 (30 min), max 3600 (1 h).
|
|
1435
|
+
*/
|
|
1436
|
+
maxSeconds?: number;
|
|
1437
|
+
/** Abort signal to cancel the stream. */
|
|
1438
|
+
signal?: AbortSignal;
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* A single event from {@link AtlaSentClient.subscribeDecisions}.
|
|
1442
|
+
*
|
|
1443
|
+
* The `type` field maps to the audit-event type emitted by the server
|
|
1444
|
+
* (e.g. `"evaluate.allow"`, `"evaluate.deny"`, `"permit.verified"`).
|
|
1445
|
+
* `"heartbeat"` is a synthetic type emitted by the SDK — not a server
|
|
1446
|
+
* event — indicating the server sent a keepalive ping.
|
|
1447
|
+
* `"session_end"` signals the server-side max-seconds limit was reached;
|
|
1448
|
+
* reconnect with `lastEventId` to continue.
|
|
1449
|
+
*/
|
|
1450
|
+
interface DecisionStreamEvent {
|
|
1451
|
+
/** Stable server-assigned ID. Pass as `lastEventId` to resume. */
|
|
1452
|
+
id?: string;
|
|
1453
|
+
/**
|
|
1454
|
+
* Audit-event type, e.g. `"evaluate.allow"`, `"evaluate.deny"`,
|
|
1455
|
+
* `"evaluate.hold"`, `"permit.verified"`, `"permit.revoked"`,
|
|
1456
|
+
* `"heartbeat"`, `"session_end"`.
|
|
1457
|
+
*/
|
|
1458
|
+
type: string;
|
|
1459
|
+
decision?: DecisionCanonical;
|
|
1460
|
+
actorId?: string;
|
|
1461
|
+
resourceType?: string;
|
|
1462
|
+
resourceId?: string;
|
|
1463
|
+
payload?: Record<string, unknown>;
|
|
1464
|
+
hash?: string;
|
|
1465
|
+
previousHash?: string;
|
|
1466
|
+
occurredAt?: string;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
/**
|
|
1470
|
+
* Evidence Engine — per-decision proof artifacts, "why" traces, and
|
|
1471
|
+
* compliance-ready bundles.
|
|
1472
|
+
*
|
|
1473
|
+
* Turn every AtlaSent decision into tamper-evident proof that buyers
|
|
1474
|
+
* can hand to auditors, compliance teams, and regulators.
|
|
1475
|
+
*
|
|
1476
|
+
* Primary entry points:
|
|
1477
|
+
*
|
|
1478
|
+
* 1. `buildWhyTrace(decision, reasons, constraintTrace)` — converts
|
|
1479
|
+
* the ConstraintTrace from `?include=constraint_trace` into a
|
|
1480
|
+
* structured human/machine-readable "why allowed / why denied" trace.
|
|
1481
|
+
*
|
|
1482
|
+
* 2. `buildDecisionReceiptPayload(args)` — assembles the canonical
|
|
1483
|
+
* signable payload for a per-decision receipt.
|
|
1484
|
+
*
|
|
1485
|
+
* 3. `signDecisionReceiptHmac(payload, secret)` — HMAC-SHA256 sign.
|
|
1486
|
+
*
|
|
1487
|
+
* 4. `verifyDecisionReceiptHmac(receipt, secret)` — offline verify.
|
|
1488
|
+
*
|
|
1489
|
+
* 5. `computeBundleHash(bundle)` — SHA-256 of an ActionEvidenceBundle.
|
|
1490
|
+
*
|
|
1491
|
+
* 6. `soc2ControlCoverageForDecision(opts)` — map a decision to SOC 2
|
|
1492
|
+
* control coverage.
|
|
1493
|
+
*
|
|
1494
|
+
* The {@link DecisionReceipt} is the category-defining artifact: a
|
|
1495
|
+
* self-contained, signed, human-readable proof that a specific action
|
|
1496
|
+
* was (or was not) authorized at a specific moment. Every enforcement
|
|
1497
|
+
* adapter produces one; every compliance bundle includes one.
|
|
1498
|
+
*/
|
|
1499
|
+
|
|
1500
|
+
/**
|
|
1501
|
+
* One evaluated stage within a policy, in the order the engine ran it.
|
|
1502
|
+
*/
|
|
1503
|
+
interface WhyStage {
|
|
1504
|
+
/** Engine stage name (e.g. `"role_check"`, `"context"`). */
|
|
1505
|
+
stage: string;
|
|
1506
|
+
/** Rule identifier, if the stage is rule-bound. */
|
|
1507
|
+
rule?: string;
|
|
1508
|
+
/** Whether this stage's predicate fired / matched. */
|
|
1509
|
+
matched: boolean;
|
|
1510
|
+
/** Non-obvious detail from the engine. */
|
|
1511
|
+
detail?: string;
|
|
1512
|
+
/**
|
|
1513
|
+
* Impact classification:
|
|
1514
|
+
* - `"terminal"` — this stage caused the outer decision.
|
|
1515
|
+
* - `"contributing"` — matched but was not the decisive stage.
|
|
1516
|
+
* - `"passing"` — did not match; execution continued.
|
|
1517
|
+
*/
|
|
1518
|
+
impact: "terminal" | "contributing" | "passing";
|
|
1519
|
+
}
|
|
1520
|
+
/** Per-policy evaluation block within a WhyTrace. */
|
|
1521
|
+
interface WhyPolicyEvaluation {
|
|
1522
|
+
policy_id: string;
|
|
1523
|
+
/** Policy-level decision. */
|
|
1524
|
+
decision: string;
|
|
1525
|
+
/** Engine-side fingerprint of the policy bundle row. */
|
|
1526
|
+
fingerprint: string;
|
|
1527
|
+
/** Optional risk score from a `risk` rule clause. */
|
|
1528
|
+
risk_score?: number;
|
|
1529
|
+
/** Stages evaluated for this policy, in order. */
|
|
1530
|
+
stages: WhyStage[];
|
|
1531
|
+
/** `true` iff this policy's decision drove the outer envelope decision. */
|
|
1532
|
+
was_decisive: boolean;
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Structured "why allowed / why denied" trace.
|
|
1536
|
+
*
|
|
1537
|
+
* Produced by `buildWhyTrace()` from the `ConstraintTrace` returned
|
|
1538
|
+
* by `/v1-evaluate?include=constraint_trace`. Suitable for:
|
|
1539
|
+
*
|
|
1540
|
+
* - UI display ("Why was this denied?")
|
|
1541
|
+
* - Email / Slack notifications
|
|
1542
|
+
* - Compliance-bundle human-readable section
|
|
1543
|
+
* - Machine-readable policy audit by external verifiers
|
|
1544
|
+
*
|
|
1545
|
+
* `summary` is a one-sentence plain-English explanation.
|
|
1546
|
+
*/
|
|
1547
|
+
interface WhyTrace {
|
|
1548
|
+
decision: DecisionCanonical;
|
|
1549
|
+
/** One-sentence human-readable explanation. */
|
|
1550
|
+
summary: string;
|
|
1551
|
+
/** Policy whose decision drove the outer result. Absent on clean allow. */
|
|
1552
|
+
matched_policy_id?: string;
|
|
1553
|
+
/** Per-policy evaluation blocks in evaluation order. */
|
|
1554
|
+
policy_evaluations: WhyPolicyEvaluation[];
|
|
1555
|
+
/**
|
|
1556
|
+
* The single stage that caused the terminal outcome, extracted for
|
|
1557
|
+
* quick access. `undefined` on a clean allow (no blocking stage).
|
|
1558
|
+
*/
|
|
1559
|
+
terminal_stage?: WhyStage;
|
|
1560
|
+
/** Total stages evaluated across all policies. */
|
|
1561
|
+
total_stages_evaluated: number;
|
|
1562
|
+
}
|
|
1563
|
+
/** Signing algorithm tag on a {@link DecisionReceipt}. */
|
|
1564
|
+
type DecisionReceiptAlgorithm = "hmac-sha256" | "ed25519" | "none";
|
|
1565
|
+
/**
|
|
1566
|
+
* The canonical signed payload of a {@link DecisionReceipt}.
|
|
1567
|
+
*
|
|
1568
|
+
* Field order is load-bearing: HMAC and chain verifiers stringify
|
|
1569
|
+
* this object and must reproduce byte-identical output. Never reorder
|
|
1570
|
+
* the fields; add new optional fields at the end only.
|
|
1571
|
+
*/
|
|
1572
|
+
interface DecisionReceiptPayload {
|
|
1573
|
+
receipt_id: string;
|
|
1574
|
+
evaluation_id: string;
|
|
1575
|
+
org_id: string;
|
|
1576
|
+
decision: DecisionCanonical;
|
|
1577
|
+
action: string;
|
|
1578
|
+
actor: string;
|
|
1579
|
+
resource_type: string | null;
|
|
1580
|
+
resource_id: string | null;
|
|
1581
|
+
reasons: string[];
|
|
1582
|
+
/** One-sentence human-readable summary from the WhyTrace. */
|
|
1583
|
+
why_summary: string;
|
|
1584
|
+
/** Permit ID when the decision was `"allow"`. */
|
|
1585
|
+
permit_id: string | null;
|
|
1586
|
+
/** Permit verification hash when the decision was `"allow"`. */
|
|
1587
|
+
permit_hash: string | null;
|
|
1588
|
+
/** Hash-chained audit-trail entry from the evaluate response. */
|
|
1589
|
+
audit_hash: string;
|
|
1590
|
+
/** SHA-256 hex of canonical JSON of the evaluate context. */
|
|
1591
|
+
context_hash: string;
|
|
1592
|
+
/** ISO-8601 when this receipt was issued. */
|
|
1593
|
+
issued_at: string;
|
|
1594
|
+
/** ISO-8601 TTL, or `null` for non-expiring receipts. */
|
|
1595
|
+
expires_at: string | null;
|
|
1596
|
+
}
|
|
1597
|
+
/**
|
|
1598
|
+
* A signed, tamper-evident record of a single AtlaSent authorization
|
|
1599
|
+
* decision. Self-contained: contains everything an auditor needs to
|
|
1600
|
+
* verify the decision without querying the API.
|
|
1601
|
+
*
|
|
1602
|
+
* **Signature semantics (HMAC-SHA256):**
|
|
1603
|
+
*
|
|
1604
|
+
* `HMAC-SHA256(secret,
|
|
1605
|
+
* receipt_id + "\\n" + issued_at + "\\n" + JSON.stringify(payload))`
|
|
1606
|
+
*
|
|
1607
|
+
* When `algorithm === "ed25519"`, `signature` is hex-encoded Ed25519
|
|
1608
|
+
* over the same input string encoded as UTF-8.
|
|
1609
|
+
*
|
|
1610
|
+
* Offline verification: `verifyDecisionReceiptHmac(receipt, secret)`.
|
|
1611
|
+
*
|
|
1612
|
+
* Callers MUST reject receipts where `algorithm === "none"` in any
|
|
1613
|
+
* context requiring tamper-evidence.
|
|
1614
|
+
*/
|
|
1615
|
+
interface DecisionReceipt {
|
|
1616
|
+
receipt_id: string;
|
|
1617
|
+
evaluation_id: string;
|
|
1618
|
+
org_id: string;
|
|
1619
|
+
decision: DecisionCanonical;
|
|
1620
|
+
action: string;
|
|
1621
|
+
actor: string;
|
|
1622
|
+
resource_type: string | null;
|
|
1623
|
+
resource_id: string | null;
|
|
1624
|
+
reasons: string[];
|
|
1625
|
+
/**
|
|
1626
|
+
* Full structured "why" trace. `null` when the evaluation was
|
|
1627
|
+
* performed without `?include=constraint_trace`.
|
|
1628
|
+
*/
|
|
1629
|
+
why_trace: WhyTrace | null;
|
|
1630
|
+
permit_id: string | null;
|
|
1631
|
+
permit_hash: string | null;
|
|
1632
|
+
audit_hash: string;
|
|
1633
|
+
/** SHA-256 hex of canonical JSON of the evaluate context. */
|
|
1634
|
+
context_hash: string;
|
|
1635
|
+
issued_at: string;
|
|
1636
|
+
expires_at: string | null;
|
|
1637
|
+
algorithm: DecisionReceiptAlgorithm;
|
|
1638
|
+
/**
|
|
1639
|
+
* Hex (HMAC-SHA256 or Ed25519) signature, or `null` when
|
|
1640
|
+
* `algorithm === "none"`.
|
|
1641
|
+
*/
|
|
1642
|
+
signature: string | null;
|
|
1643
|
+
/** Registry key ID that signed, when `algorithm !== "none"`. */
|
|
1644
|
+
signing_key_id: string | null;
|
|
1645
|
+
/**
|
|
1646
|
+
* Full payload that was signed. Pass to `verifyDecisionReceiptHmac`
|
|
1647
|
+
* or reconstruct independently for external verification.
|
|
1648
|
+
*/
|
|
1649
|
+
payload: DecisionReceiptPayload;
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
/** Input to {@link protect}. Same shape as `EvaluateRequest`. */
|
|
1653
|
+
interface ProtectRequest {
|
|
1654
|
+
agent: string;
|
|
1655
|
+
action: string;
|
|
1656
|
+
context?: Record<string, unknown>;
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Success return from {@link protect}. The action is authorized
|
|
1660
|
+
* end-to-end — evaluation allowed AND the resulting permit verified.
|
|
1661
|
+
*/
|
|
1662
|
+
interface Permit {
|
|
1663
|
+
/** Opaque permit / decision identifier. */
|
|
1664
|
+
permitId: string;
|
|
1665
|
+
/** Verification hash bound to the permit. */
|
|
1666
|
+
permitHash: string;
|
|
1667
|
+
/** Audit-trail entry associated with the decision (hash-chained). */
|
|
1668
|
+
auditHash: string;
|
|
1669
|
+
/** Human-readable reason from the policy engine. */
|
|
1670
|
+
reason: string;
|
|
1671
|
+
/** ISO 8601 timestamp of the verification. */
|
|
1672
|
+
timestamp: string;
|
|
1673
|
+
/** ISO-8601 expiration timestamp of the permit. null on pre-rollout servers. */
|
|
1674
|
+
permitExpiresAt: string | null;
|
|
1675
|
+
}
|
|
1676
|
+
/** Configuration for the process-wide singleton used by {@link protect}. */
|
|
1677
|
+
interface ConfigureOptions {
|
|
1678
|
+
/** Overrides `ATLASENT_API_KEY` env var. */
|
|
1679
|
+
apiKey?: string;
|
|
1680
|
+
/** Overrides the default `https://api.atlasent.io`. */
|
|
1681
|
+
baseUrl?: string;
|
|
1682
|
+
/** Per-request timeout in ms. */
|
|
1683
|
+
timeoutMs?: number;
|
|
1684
|
+
/** Inject a custom fetch (primarily for tests). */
|
|
1685
|
+
fetch?: typeof fetch;
|
|
1686
|
+
/** Override the retry policy. Pass `{ maxAttempts: 1 }` to disable retries. */
|
|
1687
|
+
retryPolicy?: RetryPolicy;
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Configure the singleton client used by {@link protect}. Optional —
|
|
1691
|
+
* if `ATLASENT_API_KEY` is set in the environment, `protect` works
|
|
1692
|
+
* without any configuration. Calling `configure` again replaces the
|
|
1693
|
+
* singleton; subsequent `protect` calls use the new settings.
|
|
1694
|
+
*/
|
|
1695
|
+
declare function configure(options: ConfigureOptions): void;
|
|
1696
|
+
/**
|
|
1697
|
+
* Run the canonical Deploy Gate V1 helper using the process-wide client.
|
|
1698
|
+
* Defaults to action `production.deploy`; execution is allowed only after
|
|
1699
|
+
* server-side `/v1-evaluate` and `/v1-verify-permit` both pass.
|
|
1700
|
+
*/
|
|
1701
|
+
declare function deployGate(request?: DeployGateRequest): Promise<DeployGateResponse>;
|
|
1702
|
+
/**
|
|
1703
|
+
* Authorize an action end-to-end. On allow, returns a verified
|
|
1704
|
+
* {@link Permit}. On anything else, throws:
|
|
1705
|
+
*
|
|
1706
|
+
* - {@link AtlaSentDeniedError} — policy denied, or the permit
|
|
1707
|
+
* failed verification. Fail-closed: if this throws, the action
|
|
1708
|
+
* MUST NOT proceed.
|
|
1709
|
+
* - {@link AtlaSentError} — transport, timeout, auth, rate-limit,
|
|
1710
|
+
* or server error. Same fail-closed contract: do not proceed.
|
|
1711
|
+
*/
|
|
1712
|
+
declare function protect(request: ProtectRequest): Promise<Permit>;
|
|
1713
|
+
/**
|
|
1714
|
+
* A verified {@link Permit} with an embedded signed {@link DecisionReceipt}.
|
|
1715
|
+
*
|
|
1716
|
+
* Returned by {@link protectWithEvidence}. Store `receipt` alongside
|
|
1717
|
+
* your action record (deploy logs, payment records, close workflows)
|
|
1718
|
+
* to give auditors a self-contained proof of authorization.
|
|
1719
|
+
*/
|
|
1720
|
+
interface PermitWithEvidence extends Permit {
|
|
1721
|
+
/** Signed per-decision receipt. `algorithm: "none"` when no signing secret was supplied. */
|
|
1722
|
+
receipt: DecisionReceipt;
|
|
1723
|
+
}
|
|
1724
|
+
/** Options for {@link protectWithEvidence}. */
|
|
1725
|
+
interface ProtectWithEvidenceOptions {
|
|
1726
|
+
/**
|
|
1727
|
+
* HMAC-SHA256 signing secret. When provided, the receipt is signed
|
|
1728
|
+
* and can be verified offline with `verifyDecisionReceiptHmac`.
|
|
1729
|
+
* Recommend `process.env.ATLASENT_RECEIPT_SIGNING_SECRET`.
|
|
1730
|
+
*/
|
|
1731
|
+
signingSecret?: string;
|
|
1732
|
+
/**
|
|
1733
|
+
* Registry key ID recorded on the receipt, paired with `signingSecret`.
|
|
1734
|
+
* Used for key rotation: store the ID alongside the receipt so
|
|
1735
|
+
* verifiers know which key to use.
|
|
1736
|
+
*/
|
|
1737
|
+
signingKeyId?: string;
|
|
1738
|
+
/**
|
|
1739
|
+
* If you have already called `client.evaluatePreflight()` for this
|
|
1740
|
+
* request, pass `constraintTrace` here to populate
|
|
1741
|
+
* `receipt.why_trace` with the full stage-by-stage "why" trace.
|
|
1742
|
+
* When omitted, `why_trace` is `null` on the receipt.
|
|
1743
|
+
*/
|
|
1744
|
+
constraintTrace?: ConstraintTrace | null;
|
|
1745
|
+
}
|
|
1746
|
+
/**
|
|
1747
|
+
* Authorize an action end-to-end and mint a signed {@link DecisionReceipt}.
|
|
1748
|
+
*
|
|
1749
|
+
* Same fail-closed contract as {@link protect} — throws
|
|
1750
|
+
* {@link AtlaSentDeniedError} on deny, {@link AtlaSentError} on
|
|
1751
|
+
* transport failure. The action MUST NOT proceed if this throws.
|
|
1752
|
+
*
|
|
1753
|
+
* On allow, returns the verified `Permit` plus a signed `DecisionReceipt`
|
|
1754
|
+
* that captures:
|
|
1755
|
+
* - The evaluation ID and decision
|
|
1756
|
+
* - Human-readable reasons
|
|
1757
|
+
* - Permit ID and hash
|
|
1758
|
+
* - Audit-trail hash (hash-chain link)
|
|
1759
|
+
* - SHA-256 of the evaluate context (tamper-evidence for the inputs)
|
|
1760
|
+
* - Optional "why" trace (pass `constraintTrace` from `evaluatePreflight`)
|
|
1761
|
+
*
|
|
1762
|
+
* ```ts
|
|
1763
|
+
* const { permit, receipt } = await protectWithEvidence(
|
|
1764
|
+
* { agent: "deploy-bot", action: "production.deploy", context },
|
|
1765
|
+
* {
|
|
1766
|
+
* signingSecret: process.env.ATLASENT_RECEIPT_SIGNING_SECRET,
|
|
1767
|
+
* signingKeyId: "key-v1",
|
|
1768
|
+
* },
|
|
1769
|
+
* );
|
|
1770
|
+
* // Store alongside the deployment record.
|
|
1771
|
+
* await db.deployments.create({ commitSha, permit, receipt });
|
|
1772
|
+
* ```
|
|
1773
|
+
*/
|
|
1774
|
+
declare function protectWithEvidence(request: ProtectRequest, opts?: ProtectWithEvidenceOptions): Promise<PermitWithEvidence>;
|
|
1775
|
+
|
|
1776
|
+
export { type ConstraintTrace as $, AtlaSentDeniedError as A, type BatchEvalItem as B, protect as C, type DecisionCanonical as D, type EvaluateRequest as E, deployGate as F, type GetPermitResponse as G, configure as H, type AtlaSentDecision as I, type AtlaSentDeniedErrorInit as J, type AtlaSentErrorCode as K, type ListPermitsRequest as L, type AtlaSentErrorInit as M, AtlaSentEscalateError as N, type AtlaSentEscalateErrorInit as O, type Permit as P, type AuditDecision as Q, type RateLimitState as R, type SubscribeDecisionsOptions as S, type AuditEvent as T, type AuditEventsPage as U, type VerifyPermitRequest as V, type AuditExport as W, type AuditExportSignatureStatus as X, type BvsSnapshot as Y, type ConfigureOptions as Z, type ConsentClassProjection as _, AtlaSentError as a, type ConstraintTracePolicy as a0, type ConstraintTraceStage as a1, DEFAULT_RETRY_POLICY as a2, DEPLOYMENT_PRODUCTION_ACTION as a3, DEPLOY_GATE_CODES as a4, type Decision as a5, type DeployGateContext as a6, type DeployGateDenyCode as a7, type DeployGateEvidence as a8, type DeployOverrideClaim as a9, type DeployPermitClaim as aa, type EvaluateBatchResultItem as ab, type EvaluateResponsePermit as ac, type EvaluateRiskEnvelope as ad, type EvaluateRiskEnvelopeFactor as ae, PRODUCTION_DEPLOY_ACTION as af, type PermitOutcome as ag, type PermitRecord as ah, PermitRevoked as ai, type PermitStatus as aj, type PermitWithEvidence as ak, type ProtectWithEvidenceOptions as al, type RetryPolicy as am, type StreamDecisionEvent as an, StreamParseError as ao, type StreamProgressEvent as ap, StreamTimeoutError as aq, computeBackoffMs as ar, hasAttemptsLeft as as, isRetryable as at, mergePolicy as au, normalizePermitOutcome as av, protectWithEvidence as aw, type ProtectRequest as b, type AtlaSentClientOptions as c, type EvaluateResponse as d, type BatchEvalResponse as e, type DecisionStreamEvent as f, type EvaluatePreflightResponse as g, type VerifyPermitResponse as h, type DeployGateRequest as i, type DeployGateResponse as j, type RevokePermitRequest as k, type RevokePermitResponse as l, type RevokePermitByIdInput as m, type RevokePermitByIdResponse as n, type VerifyPermitByIdResponse as o, type PermitValidResponse as p, type ListPermitsResponse as q, type ApiKeySelfResponse as r, type AuditEventsQuery as s, type AuditEventsResult as t, type AuditExportRequest as u, type AuditExportResult as v, type StreamOptions as w, type StreamEvent as x, type DecisionReceipt as y, type DecisionReceiptAlgorithm as z };
|