@atlasent/sdk 1.5.0 → 2.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/README.md +106 -27
- package/dist/hono.cjs +1507 -62
- 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 +1497 -62
- package/dist/hono.js.map +1 -1
- package/dist/index.cjs +3038 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3940 -496
- package/dist/index.d.ts +3940 -496
- package/dist/index.js +2932 -110
- package/dist/index.js.map +1 -1
- package/dist/protect-DiRVfVLq.d.cts +1329 -0
- package/dist/protect-DiRVfVLq.d.ts +1329 -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 +24 -2
- package/dist/protect-BKxcoR_2.d.cts +0 -159
- package/dist/protect-BKxcoR_2.d.ts +0 -159
package/README.md
CHANGED
|
@@ -13,32 +13,34 @@ import { AtlaSentClient } from "@atlasent/sdk";
|
|
|
13
13
|
|
|
14
14
|
const client = new AtlaSentClient({ apiKey: process.env.ATLASENT_API_KEY! });
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
action: "modify_patient_record",
|
|
19
|
-
context: { user: "dr_smith", environment: "production" },
|
|
16
|
+
const gate = await client.deployGate({
|
|
17
|
+
context: { repo: "atlasent/api", commit: process.env.GIT_SHA },
|
|
20
18
|
});
|
|
21
19
|
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
console.warn("Blocked:", result.reason);
|
|
20
|
+
if (!gate.allowed) {
|
|
21
|
+
console.error("Deploy blocked:", gate.reason);
|
|
22
|
+
process.exit(1);
|
|
26
23
|
}
|
|
24
|
+
|
|
25
|
+
// runDeploy();
|
|
27
26
|
```
|
|
28
27
|
|
|
29
|
-
That's it. `
|
|
28
|
+
That's it. `deployGate()` performs the V1 Deploy Gate sequence against `production.deploy`: `evaluate()` calls `POST /v1-evaluate`, receives a permit when allowed, then `verifyPermit()` calls `POST /v1-verify-permit` before your deployment can run. A clean `deny` is returned as a block result — network / server / auth failures are thrown.
|
|
30
29
|
|
|
31
|
-
##
|
|
30
|
+
## Simple V1 surface
|
|
32
31
|
|
|
33
32
|
```ts
|
|
34
33
|
client.evaluate({ agent, action, context? })
|
|
35
|
-
// → { decision: "
|
|
34
|
+
// → { decision: "allow" | "deny" | "hold" | "escalate", permitId, reason, auditHash, timestamp }
|
|
36
35
|
|
|
37
36
|
client.verifyPermit({ permitId, agent?, action?, context? })
|
|
38
37
|
// → { verified, outcome, permitHash, timestamp }
|
|
38
|
+
|
|
39
|
+
client.deployGate({ agent?, action?, context? })
|
|
40
|
+
// defaults action to "production.deploy" and returns { allowed, reason, evidence }
|
|
39
41
|
```
|
|
40
42
|
|
|
41
|
-
`verifyPermit()` confirms a previously-issued permit
|
|
43
|
+
`verifyPermit()` confirms a previously-issued permit server-side. Signed/offline permit artifacts never imply deployment authorization by themselves.
|
|
42
44
|
|
|
43
45
|
## CI deploy-gate pattern
|
|
44
46
|
|
|
@@ -49,11 +51,11 @@ const client = new AtlaSentClient({ apiKey: process.env.ATLASENT_API_KEY! });
|
|
|
49
51
|
|
|
50
52
|
const evaluation = await client.evaluate({
|
|
51
53
|
agent: "ci-deploy-bot",
|
|
52
|
-
action: "
|
|
54
|
+
action: "production.deploy",
|
|
53
55
|
context: { service: "billing-api", commit: process.env.GIT_SHA },
|
|
54
56
|
});
|
|
55
57
|
|
|
56
|
-
if (evaluation.decision !== "
|
|
58
|
+
if (evaluation.decision !== "allow") {
|
|
57
59
|
console.error("Deploy blocked:", evaluation.reason);
|
|
58
60
|
process.exit(1);
|
|
59
61
|
}
|
|
@@ -76,10 +78,10 @@ See [`examples/deploy-gate.ts`](./examples/deploy-gate.ts) for a complete CI-sha
|
|
|
76
78
|
|
|
77
79
|
```ts
|
|
78
80
|
new AtlaSentClient({
|
|
79
|
-
apiKey: "ask_live_...",
|
|
81
|
+
apiKey: "ask_live_...", // required
|
|
80
82
|
baseUrl: "https://api.atlasent.io", // default
|
|
81
|
-
timeoutMs: 10_000,
|
|
82
|
-
fetch: customFetch,
|
|
83
|
+
timeoutMs: 10_000, // default — per-request
|
|
84
|
+
fetch: customFetch, // default: globalThis.fetch
|
|
83
85
|
});
|
|
84
86
|
```
|
|
85
87
|
|
|
@@ -99,16 +101,16 @@ try {
|
|
|
99
101
|
}
|
|
100
102
|
```
|
|
101
103
|
|
|
102
|
-
| `err.code`
|
|
103
|
-
|
|
104
|
-
| `invalid_api_key`
|
|
105
|
-
| `forbidden`
|
|
106
|
-
| `rate_limited`
|
|
107
|
-
| `bad_request`
|
|
108
|
-
| `server_error`
|
|
109
|
-
| `timeout`
|
|
110
|
-
| `network`
|
|
111
|
-
| `bad_response`
|
|
104
|
+
| `err.code` | When it's thrown |
|
|
105
|
+
| ----------------- | ---------------------------------------- |
|
|
106
|
+
| `invalid_api_key` | HTTP 401 |
|
|
107
|
+
| `forbidden` | HTTP 403 |
|
|
108
|
+
| `rate_limited` | HTTP 429 (check `err.retryAfterMs`) |
|
|
109
|
+
| `bad_request` | HTTP 4xx (other than 401/403/429) |
|
|
110
|
+
| `server_error` | HTTP 5xx |
|
|
111
|
+
| `timeout` | `timeoutMs` exceeded |
|
|
112
|
+
| `network` | DNS / connection failure, fetch threw |
|
|
113
|
+
| `bad_response` | non-JSON body or missing required fields |
|
|
112
114
|
|
|
113
115
|
Every `AtlaSentError` carries `err.requestId` — the UUID the SDK sent as `X-Request-ID`, correlatable in your server logs.
|
|
114
116
|
|
|
@@ -122,8 +124,85 @@ Every `AtlaSentError` carries `err.requestId` — the UUID the SDK sent as `X-Re
|
|
|
122
124
|
## Requirements
|
|
123
125
|
|
|
124
126
|
- Node.js **20** or newer (native `fetch`, `AbortSignal.timeout`, `crypto.randomUUID`).
|
|
127
|
+
- **Browser:** Chrome 103+, Firefox 100+, Safari 16+, Edge 103+. The SDK uses
|
|
128
|
+
`AbortSignal.timeout` for per-request deadlines — the constructor throws a
|
|
129
|
+
clear `AtlaSentError(code: "network")` on runtimes that lack it so the failure
|
|
130
|
+
is loud rather than silent.
|
|
125
131
|
- TypeScript **5.0+** for best type-inference ergonomics (older is fine — types are plain interfaces).
|
|
126
132
|
|
|
133
|
+
## Hono middleware
|
|
134
|
+
|
|
135
|
+
Drop-in protection for [Hono](https://hono.dev) routes via the
|
|
136
|
+
`@atlasent/sdk/hono` subpath export (requires `hono` as a peer dep):
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { Hono } from "hono";
|
|
140
|
+
import { atlaSentGuard, atlaSentErrorHandler } from "@atlasent/sdk/hono";
|
|
141
|
+
|
|
142
|
+
const app = new Hono();
|
|
143
|
+
app.onError(atlaSentErrorHandler());
|
|
144
|
+
|
|
145
|
+
app.post(
|
|
146
|
+
"/deploy/:service",
|
|
147
|
+
atlaSentGuard({
|
|
148
|
+
action: (c) => `deploy_${c.req.param("service")}`,
|
|
149
|
+
agent: (c) => c.req.header("x-agent-id") ?? "anonymous",
|
|
150
|
+
context: async (c) => ({ commit: (await c.req.json()).commit }),
|
|
151
|
+
}),
|
|
152
|
+
(c) => c.json({ ok: true, permit: c.get("atlasent") }),
|
|
153
|
+
);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`atlaSentGuard` calls `protect()` under the hood — fail-closed
|
|
157
|
+
semantics. On allow it stashes a `Permit` on the context (key:
|
|
158
|
+
`"atlasent"`, override via `options.key`). On deny or transport error
|
|
159
|
+
it throws; `atlaSentErrorHandler` maps those to 403 / 503 responses
|
|
160
|
+
so every guarded route shares one error-handling path.
|
|
161
|
+
|
|
162
|
+
> **Upcoming migration:** after `@atlasent/enforce` reaches GA the
|
|
163
|
+
> guard API will change to accept a pre-constructed `Enforce` instance
|
|
164
|
+
> instead of per-route `action/agent/context` options. The current API
|
|
165
|
+
> is **not deprecated** until that ships. See the
|
|
166
|
+
> [CHANGELOG](./CHANGELOG.md) for the full before/after and
|
|
167
|
+
> [`contract/ENFORCE_PACK.md`](../contract/ENFORCE_PACK.md) for
|
|
168
|
+
> migration details.
|
|
169
|
+
|
|
170
|
+
## Browser support
|
|
171
|
+
|
|
172
|
+
The SDK is universal and works in modern browsers with no build-time changes:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { AtlaSentClient } from "@atlasent/sdk";
|
|
176
|
+
|
|
177
|
+
const client = new AtlaSentClient({
|
|
178
|
+
apiKey: import.meta.env.VITE_ATLASENT_API_KEY,
|
|
179
|
+
baseUrl: import.meta.env.VITE_ATLASENT_API_URL,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const result = await client.evaluate({
|
|
183
|
+
agent: currentUser.id,
|
|
184
|
+
action: "view_sensitive_report",
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Auth model in browser contexts.** Shipping a long-lived API key in a browser
|
|
189
|
+
bundle exposes it in DevTools and makes it replayable if exfiltrated. The
|
|
190
|
+
recommended options in increasing security order are:
|
|
191
|
+
|
|
192
|
+
- **Option B — browser-scoped keys (short term):** Create a read-only,
|
|
193
|
+
scope-restricted, IP-allowlisted key class from the AtlaSent console.
|
|
194
|
+
Safe for internal dashboards where you control the network. Not suitable
|
|
195
|
+
for public-facing apps.
|
|
196
|
+
- **Option A — session-token mode (recommended for atlasent-hosted surfaces):**
|
|
197
|
+
After SSO sign-in, the frontend obtains a short-lived (15-min) Bearer token
|
|
198
|
+
from `GET /v1-session/token` bound to the user's scopes and tenant. The SDK
|
|
199
|
+
handles token refresh transparently. See
|
|
200
|
+
[atlasent-api#144](https://github.com/AtlaSent-Systems-Inc/atlasent-api/issues/144).
|
|
201
|
+
|
|
202
|
+
The `User-Agent` header is set to `@atlasent/sdk/<version> browser` in browser
|
|
203
|
+
runtimes (browsers strip this header anyway — it's harmless) and
|
|
204
|
+
`@atlasent/sdk/<version> node/<node-version>` in Node.
|
|
205
|
+
|
|
127
206
|
## Related
|
|
128
207
|
|
|
129
208
|
- **Python SDK:** same repo, [`../python/`](../python/README.md). Wire-compatible.
|