@dorigjo/besa 0.1.0-alpha.2 → 0.1.0-beta.5
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 +304 -337
- package/dist/admit.d.ts +13 -2
- package/dist/admit.js +186 -26
- package/dist/crypto.d.ts +4 -1
- package/dist/crypto.js +127 -19
- package/dist/grant.js +50 -5
- package/dist/index.js +418 -57
- package/dist/io.d.ts +5 -0
- package/dist/io.js +97 -0
- package/dist/keystore.d.ts +16 -0
- package/dist/keystore.js +117 -0
- package/dist/manifest.js +83 -17
- package/dist/sdk.d.ts +2 -0
- package/dist/sdk.js +2 -0
- package/dist/signing.d.ts +16 -2
- package/dist/signing.js +317 -31
- package/dist/trust.d.ts +17 -0
- package/dist/trust.js +466 -0
- package/dist/types.d.ts +25 -0
- package/examples/request.json +3 -0
- package/package.json +66 -57
- package/scripts/postinstall.mjs +30 -0
package/README.md
CHANGED
|
@@ -1,345 +1,312 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
>
|
|
6
|
-
|
|
7
|
-
>
|
|
8
|
-
|
|
9
|
-
>
|
|
10
|
-
|
|
11
|
-
>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Why it matters
|
|
33
|
-
|
|
34
|
-
AI agents increasingly call external tools, APIs, MCP servers, and internal systems.
|
|
35
|
-
|
|
36
|
-
The important question is not only whether an agent can call a tool.
|
|
37
|
-
|
|
38
|
-
The important questions are:
|
|
39
|
-
|
|
40
|
-
* Which tool is the agent allowed to call?
|
|
41
|
-
* Who signed the declared capability?
|
|
42
|
-
* Has the manifest changed?
|
|
43
|
-
* Was the call allowed or denied?
|
|
44
|
-
* Is there a receipt proving the decision?
|
|
45
|
-
|
|
46
|
-
Besa turns those answers into signed artifacts.
|
|
47
|
-
|
|
48
|
-
## Quickstart
|
|
49
|
-
|
|
50
|
-
Install dependencies:
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
npm install
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Build:
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
npm run build
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Run tests:
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
npm test
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Run the smoke test:
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
npm run smoke
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
The smoke test runs the full CLI flow: build, load, sign, verify, admit allow, admit deny, and receipt creation.
|
|
75
|
-
|
|
76
|
-
## CLI commands
|
|
77
|
-
|
|
78
|
-
Load a manifest:
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
node dist/index.js load examples/manifest.yaml
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
Sign a manifest:
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
node dist/index.js sign examples/manifest.yaml
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
Verify a signed manifest:
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
node dist/index.js verify examples/manifest.signed.json
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Admit a safe tool:
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
node dist/index.js admit examples/manifest.signed.json crm.lookup
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Deny a dangerous tool:
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
node dist/index.js admit examples/manifest.signed.json crm.delete
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
Create a signed receipt:
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
node dist/index.js receipt crm.lookup
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Expected behavior:
|
|
115
|
-
|
|
116
|
-
* `crm.lookup` -> allow / `ALLOWED`
|
|
117
|
-
* `crm.delete` -> deny / `RISK_BLOCKED`
|
|
118
|
-
|
|
119
|
-
### Grant-aware admission (optional)
|
|
120
|
-
|
|
121
|
-
Besa can scope a tool call to a specific agent. Add a `grants.yaml` listing which `agentId` may use which tools:
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="site/logo.svg" alt="" width="44" height="40" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">Besa</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center"><strong>Agent Action Receipts</strong></p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
The signed-receipt layer for AI-agent tool calls.
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="https://github.com/dorigjo/besa/actions/workflows/ci.yml"><img src="https://github.com/dorigjo/besa/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/@dorigjo/besa"><img src="https://img.shields.io/npm/v/@dorigjo/besa" alt="npm" /></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
Besa creates cryptographic execution evidence for AI-agent tool calls. Every
|
|
21
|
+
admission decision is signed. Every signed receipt is tamper-evident and
|
|
22
|
+
independently verifiable.
|
|
23
|
+
|
|
24
|
+
> **Beta.** `0.1.0-beta.5` is a public developer beta. Not yet production-ready.
|
|
25
|
+
> Feedback and issues: [github.com/dorigjo/besa/issues](https://github.com/dorigjo/besa/issues).
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Trust flow
|
|
122
30
|
|
|
123
31
|
```
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
32
|
+
manifest.yaml
|
|
33
|
+
→ besa sign # sign the declared tools, capabilities, risks, scopes
|
|
34
|
+
→ manifest.signed.json # Ed25519-signed artifact
|
|
35
|
+
→ besa trust add # pin the publisher's public key
|
|
36
|
+
→ besa verify # verify signature against pinned trust anchor
|
|
37
|
+
→ besa admit <tool> # dry-run: check policy, capabilities, budget
|
|
38
|
+
→ besa receipt <tool> # enforce budget, issue signed receipt
|
|
39
|
+
→ besa verify-receipt # verify the receipt chain end-to-end
|
|
128
40
|
```
|
|
129
41
|
|
|
130
|
-
|
|
42
|
+
Every step produces a durable, verifiable artifact. The signed manifest, public
|
|
43
|
+
key ID, manifest hash, admission decision, request hash, and signed receipt are
|
|
44
|
+
all tamper-evident. Changing any field causes verification to fail.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## What works today
|
|
131
49
|
|
|
50
|
+
- YAML + JSON manifest loading with strict schema validation
|
|
51
|
+
- Ed25519 key generation; AES-256-GCM encrypted key storage with scrypt KDF
|
|
52
|
+
- Whole-envelope Ed25519 signatures covering manifest, hash, key, algorithm, and timestamp
|
|
53
|
+
- Explicit public-key trust anchors (active / retired / revoked)
|
|
54
|
+
- Signed key-rotation proofs preserving forward trust continuity
|
|
55
|
+
- Allow / deny decisions with machine-readable reason codes
|
|
56
|
+
- Destructive high-risk tool blocking by default policy
|
|
57
|
+
- ASCII-validated tool names (prevents Unicode homograph attacks)
|
|
58
|
+
- Manifest-scoped call budgets with cross-process atomic file locking
|
|
59
|
+
- Optional per-agent grant scoping
|
|
60
|
+
- Signed, tamper-evident Agent Action Receipts
|
|
61
|
+
- Receipt trust-chain verification
|
|
62
|
+
- TypeScript SDK exports
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Install
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm install @dorigjo/besa
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Pin the beta channel explicitly:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm install @dorigjo/besa@beta
|
|
132
76
|
```
|
|
133
|
-
|
|
77
|
+
|
|
78
|
+
Set the key passphrase before any signing operation:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
export BESA_KEY_PASSPHRASE="your-passphrase-at-least-16-bytes"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Build from source
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
git clone https://github.com/dorigjo/besa
|
|
88
|
+
cd besa
|
|
89
|
+
npm ci
|
|
90
|
+
npm run build
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Quickstart
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Show available commands
|
|
99
|
+
npx besa --help
|
|
100
|
+
|
|
101
|
+
# Generate or load the local signing key
|
|
102
|
+
npx besa keys
|
|
103
|
+
|
|
104
|
+
# Validate the manifest (dry-run, no signing)
|
|
105
|
+
npx besa load examples/manifest.yaml
|
|
106
|
+
|
|
107
|
+
# Sign the manifest
|
|
108
|
+
npx besa sign examples/manifest.yaml
|
|
109
|
+
|
|
110
|
+
# Verify the signature
|
|
111
|
+
npx besa verify examples/manifest.signed.json
|
|
112
|
+
|
|
113
|
+
# Admission dry-run (does not consume budget)
|
|
114
|
+
npx besa admit examples/manifest.signed.json crm.lookup # → allow
|
|
115
|
+
npx besa admit examples/manifest.signed.json crm.delete # → deny RISK_BLOCKED
|
|
116
|
+
|
|
117
|
+
# Issue a signed receipt (consumes budget)
|
|
118
|
+
npx besa receipt crm.lookup examples/manifest.signed.json \
|
|
119
|
+
--request examples/request.json
|
|
120
|
+
|
|
121
|
+
# Verify the receipt chain
|
|
122
|
+
npx besa verify-receipt .besa/receipts/<id>.json \
|
|
123
|
+
examples/manifest.signed.json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### PowerShell
|
|
127
|
+
|
|
128
|
+
```powershell
|
|
129
|
+
$env:BESA_KEY_PASSPHRASE = "your-passphrase-at-least-16-bytes"
|
|
130
|
+
npx besa keys
|
|
131
|
+
npx besa sign examples/manifest.yaml
|
|
132
|
+
npx besa verify examples/manifest.signed.json
|
|
133
|
+
npx besa admit examples/manifest.signed.json crm.lookup
|
|
134
|
+
npx besa receipt crm.lookup examples/manifest.signed.json `
|
|
135
|
+
--request examples/request.json
|
|
136
|
+
|
|
137
|
+
$receipt = Get-ChildItem .\.besa\receipts\*.json |
|
|
138
|
+
Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
|
139
|
+
npx besa verify-receipt $receipt.FullName examples/manifest.signed.json
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Consumer trust (separate system)
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Pin the publisher's public key
|
|
146
|
+
npx besa trust add examples/manifest.signed.json \
|
|
147
|
+
--trust consumer-trust.json
|
|
148
|
+
|
|
149
|
+
# Verify against a pinned trust anchor (fails without it)
|
|
150
|
+
npx besa verify examples/manifest.signed.json \
|
|
151
|
+
--trust consumer-trust.json
|
|
134
152
|
```
|
|
135
153
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
* no key rotation
|
|
296
|
-
* no key revocation
|
|
297
|
-
* one default policy
|
|
298
|
-
|
|
299
|
-
Default policy:
|
|
300
|
-
|
|
301
|
-
* destructive + high risk = denied
|
|
302
|
-
|
|
303
|
-
## What Besa is not
|
|
304
|
-
|
|
305
|
-
Besa is currently an alpha trust layer for AI-agent tool control and evidence.
|
|
306
|
-
|
|
307
|
-
It is not:
|
|
308
|
-
|
|
309
|
-
* a hosted SaaS
|
|
310
|
-
* a dashboard or UI
|
|
311
|
-
* a full MCP gateway
|
|
312
|
-
* production key management
|
|
313
|
-
* a compliance certification product
|
|
314
|
-
* a replacement for identity, authorization, audit storage, or security monitoring
|
|
315
|
-
* ready for production secrets or production systems
|
|
316
|
-
|
|
317
|
-
## Release docs
|
|
318
|
-
|
|
319
|
-
* [SECURITY.md](SECURITY.md) — security policy, key handling, and vulnerability reporting
|
|
320
|
-
* [docs/THREAT_MODEL.md](docs/THREAT_MODEL.md) — assets, threats, mitigations, and current MVP limitations
|
|
321
|
-
* [docs/RELEASE_CHECKLIST.md](docs/RELEASE_CHECKLIST.md) — pre-release gates before tagging or publishing
|
|
322
|
-
* [CHANGELOG.md](CHANGELOG.md) — notable changes by version
|
|
323
|
-
|
|
324
|
-
## Roadmap
|
|
325
|
-
|
|
326
|
-
Planned next layers:
|
|
327
|
-
|
|
328
|
-
* hosted key management
|
|
329
|
-
* remote verifier API
|
|
330
|
-
* policy packs
|
|
331
|
-
* MCP gateway integration
|
|
332
|
-
* enterprise audit export
|
|
333
|
-
* receipts API
|
|
334
|
-
* usage-based ActionMeter
|
|
335
|
-
* organization-level trust registry
|
|
336
|
-
|
|
337
|
-
## Positioning
|
|
338
|
-
|
|
339
|
-
Besa is signed trust infrastructure for AI-agent tools.
|
|
340
|
-
|
|
341
|
-
It is not another chatbot.
|
|
342
|
-
|
|
343
|
-
It is not another dashboard.
|
|
344
|
-
|
|
345
|
-
It is a trust layer for agentic execution.
|
|
154
|
+
### Key rotation
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npx besa keys rotate
|
|
158
|
+
|
|
159
|
+
npx besa trust apply .besa/rotations/<rotation>.json \
|
|
160
|
+
--trust consumer-trust.json
|
|
161
|
+
|
|
162
|
+
npx besa sign examples/manifest.yaml # re-sign under the new key
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The previous key becomes `retired`: artifacts signed before rotation remain
|
|
166
|
+
verifiable, but new admissions under that key are denied. `trust revoke`
|
|
167
|
+
invalidates a key for all artifacts, current and historical.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Commands
|
|
172
|
+
|
|
173
|
+
| Command | Description |
|
|
174
|
+
|---|---|
|
|
175
|
+
| `besa keys` | Generate or display the local signing key |
|
|
176
|
+
| `besa keys rotate` | Rotate to a new key, archive the previous |
|
|
177
|
+
| `besa trust add <manifest>` | Pin the manifest's public key as a trust anchor |
|
|
178
|
+
| `besa trust apply <rotation>` | Apply a signed rotation proof |
|
|
179
|
+
| `besa trust revoke <key-id>` | Revoke a trust anchor |
|
|
180
|
+
| `besa trust list` | List all trust anchors and their status |
|
|
181
|
+
| `besa load <manifest>` | Validate a manifest without signing |
|
|
182
|
+
| `besa sign <manifest>` | Sign a manifest |
|
|
183
|
+
| `besa verify <manifest>` | Verify a signed manifest against the trust store |
|
|
184
|
+
| `besa admit <manifest> <tool>` | Dry-run: check policy + budget |
|
|
185
|
+
| `besa receipt <tool> <manifest>` | Enforce budget and issue a signed receipt |
|
|
186
|
+
| `besa verify-receipt <receipt> <manifest>` | Verify the receipt trust chain |
|
|
187
|
+
|
|
188
|
+
All commands accept `--trust <trust.json>` to use a consumer-side trust store.
|
|
189
|
+
`admit` and `receipt` also accept `--agent <id> --grants <grants.yaml>`.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Receipt artifact
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"receiptId": "rcpt_2d7942c7-8f70-4984-9c3f-24876acfd860",
|
|
198
|
+
"manifestHash": "ea7e9ca22d199f40281cdf9e5d6145440c6c7d6bfbe94157c4b1da5527054410",
|
|
199
|
+
"toolName": "crm.lookup",
|
|
200
|
+
"decision": "allow",
|
|
201
|
+
"reasonCode": "ALLOWED",
|
|
202
|
+
"timestamp": "2026-06-19T10:00:00.000Z",
|
|
203
|
+
"requestHash": "b27b80d1227c167a6fca199778645daa77d20a8087782fc48802d11d6281c920",
|
|
204
|
+
"publicKeyId": "f68668614543c4896cf8cee418492f1a4df1f1acdba8850f94728b8a94cf90fe",
|
|
205
|
+
"algorithm": "ed25519",
|
|
206
|
+
"signature": "<base64-ed25519-signature>"
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`publicKeyId` is the full SHA-256 fingerprint of the Ed25519 public key DER bytes.
|
|
211
|
+
Changing any field causes `verify-receipt` to fail closed.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Reason codes
|
|
216
|
+
|
|
217
|
+
| Code | Meaning |
|
|
218
|
+
|---|---|
|
|
219
|
+
| `ALLOWED` | Tool call admitted |
|
|
220
|
+
| `TOOL_NOT_FOUND` | Tool not declared in the signed manifest |
|
|
221
|
+
| `RISK_BLOCKED` | Destructive high-risk tool blocked by policy |
|
|
222
|
+
| `BUDGET_EXCEEDED` | Call count reached the manifest budget limit |
|
|
223
|
+
| `TOOL_NOT_GRANTED` | Agent not granted access to this tool |
|
|
224
|
+
| `AGENT_NOT_FOUND` | Agent ID not listed in the grant set |
|
|
225
|
+
| `E_KEY_UNTRUSTED` | Signing key not in the trust store |
|
|
226
|
+
| `E_KEY_RETIRED` | Key retired; new admissions under it are denied |
|
|
227
|
+
| `E_KEY_REVOKED` | Key revoked; all operations denied |
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## SDK
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import {
|
|
235
|
+
admit,
|
|
236
|
+
addTrustAnchor,
|
|
237
|
+
applyKeyRotation,
|
|
238
|
+
canonicalize,
|
|
239
|
+
checkTrustedKey,
|
|
240
|
+
createKeyRotation,
|
|
241
|
+
createReceipt,
|
|
242
|
+
generateKeyPair,
|
|
243
|
+
hashRequest,
|
|
244
|
+
loadManifest,
|
|
245
|
+
signManifest,
|
|
246
|
+
validateManifest,
|
|
247
|
+
validateReceipt,
|
|
248
|
+
verifyReceiptDetailed,
|
|
249
|
+
verifySignedManifest,
|
|
250
|
+
verifyTrustedSignedManifest,
|
|
251
|
+
} from "@dorigjo/besa";
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Security model
|
|
257
|
+
|
|
258
|
+
Besa provides **tamper-evidence**, not secrecy.
|
|
259
|
+
|
|
260
|
+
A signed manifest proves that the declared tool capabilities, scopes, risks, and
|
|
261
|
+
metadata have not changed since signing. A signed receipt creates a
|
|
262
|
+
tamper-evident record that a specific admission decision was made at a specific
|
|
263
|
+
time under a specific key.
|
|
264
|
+
|
|
265
|
+
**Cryptography:**
|
|
266
|
+
|
|
267
|
+
- Ed25519 signatures (256-bit security) on the complete artifact envelope
|
|
268
|
+
- AES-256-GCM key encryption at rest with scrypt KDF (N=32768, r=8, p=1)
|
|
269
|
+
- SHA-256 manifest hashing and full 256-bit (64-hex-character) SHA-256 public key fingerprints
|
|
270
|
+
- Domain-separated signature messages (`besa:<domain>:v1\0<canonical-json>`)
|
|
271
|
+
- Timing-safe public key comparison via `crypto.timingSafeEqual`
|
|
272
|
+
|
|
273
|
+
**Fail-closed behavior:**
|
|
274
|
+
|
|
275
|
+
- Verification fails on any signature, hash, or key mismatch
|
|
276
|
+
- Admission fails closed on invalid policy, manifest, or call count
|
|
277
|
+
- Trust store rejects symlinks, unknown fields, and duplicate key IDs
|
|
278
|
+
- Trust store paths must end in `.json`
|
|
279
|
+
- Tool names are restricted to ASCII printable characters
|
|
280
|
+
|
|
281
|
+
See [SECURITY.md](SECURITY.md) and [docs/THREAT_MODEL.md](docs/THREAT_MODEL.md).
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Release gates
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
npm ci
|
|
289
|
+
npm run build
|
|
290
|
+
npm test # 56 tests
|
|
291
|
+
npm run smoke # end-to-end trust flow
|
|
292
|
+
npm run test:package
|
|
293
|
+
npm pack --dry-run
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Beta limitations
|
|
299
|
+
|
|
300
|
+
- Local key storage only; no hosted key management or HSM integration
|
|
301
|
+
- File-based meter and trust state; intended for single-host use
|
|
302
|
+
- No distributed replay protection across machines or environments
|
|
303
|
+
- No external trusted timestamp authority
|
|
304
|
+
- No hosted verifier, receipt retention, or SIEM export
|
|
305
|
+
- No production identity or multi-user authorization
|
|
306
|
+
- No formal compliance certification (SOC 2, ISO 27001, EU AI Act)
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## License
|
|
311
|
+
|
|
312
|
+
[MIT](LICENSE)
|
package/dist/admit.d.ts
CHANGED
|
@@ -4,15 +4,26 @@ export declare const REASON: {
|
|
|
4
4
|
readonly TOOL_NOT_FOUND: "TOOL_NOT_FOUND";
|
|
5
5
|
readonly RISK_BLOCKED: "RISK_BLOCKED";
|
|
6
6
|
readonly BUDGET_EXCEEDED: "BUDGET_EXCEEDED";
|
|
7
|
+
readonly INVALID_MANIFEST: "INVALID_MANIFEST";
|
|
8
|
+
readonly INVALID_TOOL_NAME: "INVALID_TOOL_NAME";
|
|
9
|
+
readonly INVALID_CALL_COUNT: "INVALID_CALL_COUNT";
|
|
10
|
+
readonly INVALID_POLICY: "INVALID_POLICY";
|
|
7
11
|
};
|
|
8
12
|
export interface AdmissionPolicy {
|
|
9
13
|
denyDestructiveHighRisk: boolean;
|
|
10
14
|
}
|
|
11
15
|
export declare const DEFAULT_POLICY: AdmissionPolicy;
|
|
12
16
|
export type MeterState = Record<string, number>;
|
|
17
|
+
export interface MeterLockOptions {
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
staleMs?: number;
|
|
20
|
+
retryMs?: number;
|
|
21
|
+
}
|
|
13
22
|
export declare function findTool(manifest: Manifest, toolName: string): ToolDefinition | undefined;
|
|
14
23
|
export declare function admit(manifest: Manifest, toolName: string, callCount: number, policy?: AdmissionPolicy): AdmissionDecision;
|
|
24
|
+
export declare function meterKey(manifestHash: string, toolName: string): string;
|
|
15
25
|
export declare function loadMeter(path: string): MeterState;
|
|
16
26
|
export declare function saveMeter(path: string, state: MeterState): void;
|
|
17
|
-
export declare function getCount(state: MeterState,
|
|
18
|
-
export declare function increment(state: MeterState,
|
|
27
|
+
export declare function getCount(state: MeterState, key: string): number;
|
|
28
|
+
export declare function increment(state: MeterState, key: string): MeterState;
|
|
29
|
+
export declare function admitAndConsume(path: string, manifestHash: string, manifest: Manifest, toolName: string, policy?: AdmissionPolicy, lockOptions?: MeterLockOptions): AdmissionDecision;
|