@dorigjo/besa 0.1.0-alpha.2 → 0.1.0-beta.4
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 +310 -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,318 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
> **Alpha / developer preview — not production-ready.**
|
|
6
|
-
>
|
|
7
|
-
> Besa is currently an early alpha (`0.1.0-alpha.0`). APIs, file formats, receipt formats, and behavior may change without notice.
|
|
8
|
-
>
|
|
9
|
-
> Do not use Besa to protect production systems, production secrets, customer data, or real signing keys yet.
|
|
10
|
-
>
|
|
11
|
-
> The key under `.besa/` is a local demo key.
|
|
12
|
-
|
|
13
|
-
Besa signs MCP-style tool manifests, verifies them before use, admits or denies tool calls against policy, and issues signed tamper-evident receipts.
|
|
14
|
-
|
|
15
|
-
Besa is the trust layer for AI-agent tools.
|
|
16
|
-
|
|
17
|
-
## What it does
|
|
18
|
-
|
|
19
|
-
* Signs tool manifests with Ed25519.
|
|
20
|
-
* Verifies signed manifests before runtime use.
|
|
21
|
-
* Allows or denies tool calls with reason codes.
|
|
22
|
-
* Blocks destructive high-risk tools by default.
|
|
23
|
-
* Tracks local per-tool usage with a mini ActionMeter.
|
|
24
|
-
* Creates signed receipts for admission decisions.
|
|
25
|
-
|
|
26
|
-
Flow:
|
|
27
|
-
|
|
28
|
-
```text
|
|
29
|
-
manifest.yaml -> sign -> verify -> admit -> receipt
|
|
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>
|
|
122
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
|
+
<img src="https://img.shields.io/badge/public_release-coming_soon-C1121F?labelColor=003049" alt="Public release coming soon" />
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Early Access
|
|
22
|
+
|
|
23
|
+
Besa 0.1.0-beta.4 is available as a GitHub Release tarball while npm publishing is pending.
|
|
24
|
+
|
|
25
|
+
Install:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install https://github.com/dorigjo/besa/releases/download/v0.1.0-beta.4/dorigjo-besa-0.1.0-beta.4.tgz
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
See [EARLY_ACCESS.md](EARLY_ACCESS.md) for integrity hash, quickstart, and known limitations.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
Besa creates cryptographic execution evidence for AI-agent tool calls. Every
|
|
36
|
+
admission decision is signed. Every signed receipt is tamper-evident and
|
|
37
|
+
independently verifiable.
|
|
38
|
+
|
|
39
|
+
> **Beta.** `0.1.0-beta.4` is a local developer beta. Public release coming soon.
|
|
40
|
+
> Enterprise inquiry: [open an issue](https://github.com/dorigjo/besa/issues).
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Trust flow
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
manifest.yaml
|
|
48
|
+
→ besa sign # sign the declared tools, capabilities, risks, scopes
|
|
49
|
+
→ manifest.signed.json # Ed25519-signed artifact
|
|
50
|
+
→ besa trust add # pin the publisher's public key
|
|
51
|
+
→ besa verify # verify signature against pinned trust anchor
|
|
52
|
+
→ besa admit <tool> # dry-run: check policy, capabilities, budget
|
|
53
|
+
→ besa receipt <tool> # enforce budget, issue signed receipt
|
|
54
|
+
→ besa verify-receipt # verify the receipt chain end-to-end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Every step produces a durable, verifiable artifact. The signed manifest, public
|
|
58
|
+
key ID, manifest hash, admission decision, request hash, and signed receipt are
|
|
59
|
+
all tamper-evident. Changing any field causes verification to fail.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## What works today
|
|
64
|
+
|
|
65
|
+
- YAML + JSON manifest loading with strict schema validation
|
|
66
|
+
- Ed25519 key generation; AES-256-GCM encrypted key storage with scrypt KDF
|
|
67
|
+
- Whole-envelope Ed25519 signatures covering manifest, hash, key, algorithm, and timestamp
|
|
68
|
+
- Explicit public-key trust anchors (active / retired / revoked)
|
|
69
|
+
- Signed key-rotation proofs preserving forward trust continuity
|
|
70
|
+
- Allow / deny decisions with machine-readable reason codes
|
|
71
|
+
- Destructive high-risk tool blocking by default policy
|
|
72
|
+
- ASCII-validated tool names (prevents Unicode homograph attacks)
|
|
73
|
+
- Manifest-scoped call budgets with cross-process atomic file locking
|
|
74
|
+
- Optional per-agent grant scoping
|
|
75
|
+
- Signed, tamper-evident Agent Action Receipts
|
|
76
|
+
- Receipt trust-chain verification
|
|
77
|
+
- TypeScript SDK exports
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Install
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npm install @dorigjo/besa@beta
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or build from source:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git clone https://github.com/dorigjo/besa
|
|
91
|
+
cd besa
|
|
92
|
+
npm ci
|
|
93
|
+
npm run build
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Set the key passphrase before any signing operation:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
export BESA_KEY_PASSPHRASE="your-passphrase-at-least-16-bytes"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Quickstart
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Generate or load the local signing key
|
|
108
|
+
node dist/index.js keys
|
|
109
|
+
|
|
110
|
+
# Validate the manifest (dry-run, no signing)
|
|
111
|
+
node dist/index.js load examples/manifest.yaml
|
|
112
|
+
|
|
113
|
+
# Sign the manifest
|
|
114
|
+
node dist/index.js sign examples/manifest.yaml
|
|
115
|
+
|
|
116
|
+
# Verify the signature
|
|
117
|
+
node dist/index.js verify examples/manifest.signed.json
|
|
118
|
+
|
|
119
|
+
# Admission dry-run (does not consume budget)
|
|
120
|
+
node dist/index.js admit examples/manifest.signed.json crm.lookup # → allow
|
|
121
|
+
node dist/index.js admit examples/manifest.signed.json crm.delete # → deny RISK_BLOCKED
|
|
122
|
+
|
|
123
|
+
# Issue a signed receipt (consumes budget)
|
|
124
|
+
node dist/index.js receipt crm.lookup examples/manifest.signed.json \
|
|
125
|
+
--request examples/request.json
|
|
126
|
+
|
|
127
|
+
# Verify the receipt chain
|
|
128
|
+
node dist/index.js verify-receipt .besa/receipts/<id>.json \
|
|
129
|
+
examples/manifest.signed.json
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### PowerShell
|
|
133
|
+
|
|
134
|
+
```powershell
|
|
135
|
+
$env:BESA_KEY_PASSPHRASE = "your-passphrase-at-least-16-bytes"
|
|
136
|
+
node .\dist\index.js keys
|
|
137
|
+
node .\dist\index.js sign .\examples\manifest.yaml
|
|
138
|
+
node .\dist\index.js verify .\examples\manifest.signed.json
|
|
139
|
+
node .\dist\index.js admit .\examples\manifest.signed.json crm.lookup
|
|
140
|
+
node .\dist\index.js receipt crm.lookup .\examples\manifest.signed.json `
|
|
141
|
+
--request .\examples\request.json
|
|
142
|
+
|
|
143
|
+
$receipt = Get-ChildItem .\.besa\receipts\*.json |
|
|
144
|
+
Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
|
145
|
+
node .\dist\index.js verify-receipt $receipt.FullName .\examples\manifest.signed.json
|
|
123
146
|
```
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
147
|
+
|
|
148
|
+
### Consumer trust (separate system)
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# Pin the publisher's public key
|
|
152
|
+
node dist/index.js trust add examples/manifest.signed.json \
|
|
153
|
+
--trust consumer-trust.json
|
|
154
|
+
|
|
155
|
+
# Verify against a pinned trust anchor (fails without it)
|
|
156
|
+
node dist/index.js verify examples/manifest.signed.json \
|
|
157
|
+
--trust consumer-trust.json
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Key rotation
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
node dist/index.js keys rotate
|
|
164
|
+
|
|
165
|
+
node dist/index.js trust apply .besa/rotations/<rotation>.json \
|
|
166
|
+
--trust consumer-trust.json
|
|
167
|
+
|
|
168
|
+
node dist/index.js sign examples/manifest.yaml # re-sign under the new key
|
|
128
169
|
```
|
|
129
170
|
|
|
130
|
-
|
|
171
|
+
The previous key becomes `retired`: artifacts signed before rotation remain
|
|
172
|
+
verifiable, but new admissions under that key are denied. `trust revoke`
|
|
173
|
+
invalidates a key for all artifacts, current and historical.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Commands
|
|
178
|
+
|
|
179
|
+
| Command | Description |
|
|
180
|
+
|---|---|
|
|
181
|
+
| `besa keys` | Generate or display the local signing key |
|
|
182
|
+
| `besa keys rotate` | Rotate to a new key, archive the previous |
|
|
183
|
+
| `besa trust add <manifest>` | Pin the manifest's public key as a trust anchor |
|
|
184
|
+
| `besa trust apply <rotation>` | Apply a signed rotation proof |
|
|
185
|
+
| `besa trust revoke <key-id>` | Revoke a trust anchor |
|
|
186
|
+
| `besa trust list` | List all trust anchors and their status |
|
|
187
|
+
| `besa load <manifest>` | Validate a manifest without signing |
|
|
188
|
+
| `besa sign <manifest>` | Sign a manifest |
|
|
189
|
+
| `besa verify <manifest>` | Verify a signed manifest against the trust store |
|
|
190
|
+
| `besa admit <manifest> <tool>` | Dry-run: check policy + budget |
|
|
191
|
+
| `besa receipt <tool> <manifest>` | Enforce budget and issue a signed receipt |
|
|
192
|
+
| `besa verify-receipt <receipt> <manifest>` | Verify the receipt trust chain |
|
|
193
|
+
|
|
194
|
+
All commands accept `--trust <trust.json>` to use a consumer-side trust store.
|
|
195
|
+
`admit` and `receipt` also accept `--agent <id> --grants <grants.yaml>`.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Receipt artifact
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"receiptId": "rcpt_2d7942c7-8f70-4984-9c3f-24876acfd860",
|
|
204
|
+
"manifestHash": "ea7e9ca22d199f40281cdf9e5d6145440c6c7d6bfbe94157c4b1da5527054410",
|
|
205
|
+
"toolName": "crm.lookup",
|
|
206
|
+
"decision": "allow",
|
|
207
|
+
"reasonCode": "ALLOWED",
|
|
208
|
+
"timestamp": "2026-06-19T10:00:00.000Z",
|
|
209
|
+
"requestHash": "b27b80d1227c167a6fca199778645daa77d20a8087782fc48802d11d6281c920",
|
|
210
|
+
"publicKeyId": "f68668614543c4896cf8cee418492f1a4df1f1acdba8850f94728b8a94cf90fe",
|
|
211
|
+
"algorithm": "ed25519",
|
|
212
|
+
"signature": "<base64-ed25519-signature>"
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
`publicKeyId` is the full SHA-256 fingerprint of the Ed25519 public key DER bytes.
|
|
217
|
+
Changing any field causes `verify-receipt` to fail closed.
|
|
218
|
+
|
|
219
|
+
---
|
|
131
220
|
|
|
221
|
+
## Reason codes
|
|
222
|
+
|
|
223
|
+
| Code | Meaning |
|
|
224
|
+
|---|---|
|
|
225
|
+
| `ALLOWED` | Tool call admitted |
|
|
226
|
+
| `TOOL_NOT_FOUND` | Tool not declared in the signed manifest |
|
|
227
|
+
| `RISK_BLOCKED` | Destructive high-risk tool blocked by policy |
|
|
228
|
+
| `BUDGET_EXCEEDED` | Call count reached the manifest budget limit |
|
|
229
|
+
| `TOOL_NOT_GRANTED` | Agent not granted access to this tool |
|
|
230
|
+
| `AGENT_NOT_FOUND` | Agent ID not listed in the grant set |
|
|
231
|
+
| `E_KEY_UNTRUSTED` | Signing key not in the trust store |
|
|
232
|
+
| `E_KEY_RETIRED` | Key retired; new admissions under it are denied |
|
|
233
|
+
| `E_KEY_REVOKED` | Key revoked; all operations denied |
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## SDK
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import {
|
|
241
|
+
admit,
|
|
242
|
+
addTrustAnchor,
|
|
243
|
+
applyKeyRotation,
|
|
244
|
+
canonicalize,
|
|
245
|
+
checkTrustedKey,
|
|
246
|
+
createKeyRotation,
|
|
247
|
+
createReceipt,
|
|
248
|
+
generateKeyPair,
|
|
249
|
+
hashRequest,
|
|
250
|
+
loadManifest,
|
|
251
|
+
signManifest,
|
|
252
|
+
validateManifest,
|
|
253
|
+
validateReceipt,
|
|
254
|
+
verifyReceiptDetailed,
|
|
255
|
+
verifySignedManifest,
|
|
256
|
+
verifyTrustedSignedManifest,
|
|
257
|
+
} from "@dorigjo/besa";
|
|
132
258
|
```
|
|
133
|
-
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Security model
|
|
263
|
+
|
|
264
|
+
Besa provides **tamper-evidence**, not secrecy.
|
|
265
|
+
|
|
266
|
+
A signed manifest proves that the declared tool capabilities, scopes, risks, and
|
|
267
|
+
metadata have not changed since signing. A signed receipt creates a
|
|
268
|
+
tamper-evident record that a specific admission decision was made at a specific
|
|
269
|
+
time under a specific key.
|
|
270
|
+
|
|
271
|
+
**Cryptography:**
|
|
272
|
+
|
|
273
|
+
- Ed25519 signatures (256-bit security) on the complete artifact envelope
|
|
274
|
+
- AES-256-GCM key encryption at rest with scrypt KDF (N=32768, r=8, p=1)
|
|
275
|
+
- SHA-256 manifest hashing and full 64-bit SHA-256 public key fingerprints
|
|
276
|
+
- Domain-separated signature messages (`besa:<domain>:v1\0<canonical-json>`)
|
|
277
|
+
- Timing-safe public key comparison via `crypto.timingSafeEqual`
|
|
278
|
+
|
|
279
|
+
**Fail-closed behavior:**
|
|
280
|
+
|
|
281
|
+
- Verification fails on any signature, hash, or key mismatch
|
|
282
|
+
- Admission fails closed on invalid policy, manifest, or call count
|
|
283
|
+
- Trust store rejects symlinks, unknown fields, and duplicate key IDs
|
|
284
|
+
- Trust store paths must end in `.json`
|
|
285
|
+
- Tool names are restricted to ASCII printable characters
|
|
286
|
+
|
|
287
|
+
See [SECURITY.md](SECURITY.md) and [docs/THREAT_MODEL.md](docs/THREAT_MODEL.md).
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Release gates
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
npm ci
|
|
295
|
+
npm run build
|
|
296
|
+
npm test # 56 tests
|
|
297
|
+
npm run smoke # end-to-end trust flow
|
|
298
|
+
npm run test:package
|
|
299
|
+
npm pack --dry-run
|
|
134
300
|
```
|
|
135
301
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
* risk
|
|
154
|
-
* scopes
|
|
155
|
-
* budgetLimit
|
|
156
|
-
* inputSchema
|
|
157
|
-
|
|
158
|
-
Capabilities:
|
|
159
|
-
|
|
160
|
-
* read
|
|
161
|
-
* write
|
|
162
|
-
* destructive
|
|
163
|
-
|
|
164
|
-
Risk levels:
|
|
165
|
-
|
|
166
|
-
* low
|
|
167
|
-
* medium
|
|
168
|
-
* high
|
|
169
|
-
|
|
170
|
-
### Signed Manifest
|
|
171
|
-
|
|
172
|
-
A manifest signed with Ed25519.
|
|
173
|
-
|
|
174
|
-
The signed manifest includes:
|
|
175
|
-
|
|
176
|
-
* manifest
|
|
177
|
-
* manifestHash
|
|
178
|
-
* algorithm
|
|
179
|
-
* publicKey
|
|
180
|
-
* publicKeyId
|
|
181
|
-
* signature
|
|
182
|
-
* signedAt
|
|
183
|
-
|
|
184
|
-
### Admission Decision
|
|
185
|
-
|
|
186
|
-
Besa evaluates whether a tool call should be allowed or denied.
|
|
187
|
-
|
|
188
|
-
Reason codes include:
|
|
189
|
-
|
|
190
|
-
* `ALLOWED`
|
|
191
|
-
* `TOOL_NOT_FOUND`
|
|
192
|
-
* `RISK_BLOCKED`
|
|
193
|
-
* `BUDGET_EXCEEDED`
|
|
194
|
-
|
|
195
|
-
### Mini ActionMeter
|
|
196
|
-
|
|
197
|
-
Besa tracks local call counts per tool.
|
|
198
|
-
|
|
199
|
-
This allows simple budget enforcement through `budgetLimit`.
|
|
200
|
-
|
|
201
|
-
### Signed Receipt
|
|
202
|
-
|
|
203
|
-
A receipt proves what decision was made.
|
|
204
|
-
|
|
205
|
-
A receipt includes:
|
|
206
|
-
|
|
207
|
-
* receiptId
|
|
208
|
-
* manifestHash
|
|
209
|
-
* toolName
|
|
210
|
-
* decision
|
|
211
|
-
* reasonCode
|
|
212
|
-
* timestamp
|
|
213
|
-
* requestHash
|
|
214
|
-
* publicKeyId
|
|
215
|
-
* algorithm
|
|
216
|
-
* signature
|
|
217
|
-
|
|
218
|
-
## SDK usage
|
|
219
|
-
|
|
220
|
-
Import Besa from the SDK:
|
|
221
|
-
|
|
222
|
-
```ts
|
|
223
|
-
import {
|
|
224
|
-
loadManifest,
|
|
225
|
-
generateKeyPair,
|
|
226
|
-
signManifest,
|
|
227
|
-
verifySignedManifest,
|
|
228
|
-
admit,
|
|
229
|
-
createReceipt,
|
|
230
|
-
verifyReceipt,
|
|
231
|
-
} from "besa";
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
Basic flow:
|
|
235
|
-
|
|
236
|
-
```ts
|
|
237
|
-
const manifest = loadManifest("examples/manifest.yaml");
|
|
238
|
-
|
|
239
|
-
const keypair = generateKeyPair();
|
|
240
|
-
|
|
241
|
-
const signed = signManifest(manifest, keypair);
|
|
242
|
-
|
|
243
|
-
const verified = verifySignedManifest(signed);
|
|
244
|
-
|
|
245
|
-
if (!verified.valid) {
|
|
246
|
-
throw new Error(verified.reasonCode);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const decision = admit(signed, "crm.lookup");
|
|
250
|
-
|
|
251
|
-
const receipt = createReceipt(signed, decision, keypair);
|
|
252
|
-
|
|
253
|
-
const receiptResult = verifyReceipt(receipt);
|
|
254
|
-
|
|
255
|
-
if (!receiptResult.valid) {
|
|
256
|
-
throw new Error(receiptResult.reasonCode);
|
|
257
|
-
}
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
## Security
|
|
261
|
-
|
|
262
|
-
Never commit `.besa/`.
|
|
263
|
-
|
|
264
|
-
The `.besa/` folder contains local trust artifacts, including the Ed25519 private key.
|
|
265
|
-
|
|
266
|
-
Ignored local artifacts:
|
|
267
|
-
|
|
268
|
-
* `.besa/`
|
|
269
|
-
* `.besa/key.json`
|
|
270
|
-
* `.besa/meter.json`
|
|
271
|
-
* `.besa/receipts/`
|
|
272
|
-
* `examples/manifest.signed.json`
|
|
273
|
-
|
|
274
|
-
The local key generated by this MVP is a demo key. Rotate keys before real usage.
|
|
275
|
-
|
|
276
|
-
See:
|
|
277
|
-
|
|
278
|
-
* [SECURITY.md](SECURITY.md)
|
|
279
|
-
* [docs/THREAT_MODEL.md](docs/THREAT_MODEL.md)
|
|
280
|
-
|
|
281
|
-
## MVP limitations
|
|
282
|
-
|
|
283
|
-
This is an MVP and alpha developer preview.
|
|
284
|
-
|
|
285
|
-
Current limitations:
|
|
286
|
-
|
|
287
|
-
* local key storage only
|
|
288
|
-
* local JSON meter only
|
|
289
|
-
* no hosted registry
|
|
290
|
-
* no SaaS backend
|
|
291
|
-
* no dashboard
|
|
292
|
-
* no remote verifier API
|
|
293
|
-
* no hosted receipts API
|
|
294
|
-
* no distributed replay protection
|
|
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.
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Beta limitations
|
|
305
|
+
|
|
306
|
+
- Local key storage only; no hosted key management or HSM integration
|
|
307
|
+
- File-based meter and trust state; intended for single-host use
|
|
308
|
+
- No distributed replay protection across machines or environments
|
|
309
|
+
- No external trusted timestamp authority
|
|
310
|
+
- No hosted verifier, receipt retention, or SIEM export
|
|
311
|
+
- No production identity or multi-user authorization
|
|
312
|
+
- No formal compliance certification (SOC 2, ISO 27001, EU AI Act)
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## License
|
|
317
|
+
|
|
318
|
+
[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;
|